├── LICENSE ├── README.md ├── evm ├── 1_independentinterpreter.go ├── 2_deploybytecodetobackend.go └── README.md ├── misc ├── exportprivate │ └── exportprivate.go ├── minipow │ ├── minipow.go │ └── minipow_test.go ├── namehash │ └── namehash.go └── sign │ ├── offlinesigner.go │ ├── sign.go │ └── txsign.go ├── p2p ├── devp2p │ ├── A1_Server.go │ ├── A2_Connect.go │ ├── A3_Events.go │ ├── A4_Message.go │ ├── A5_Reply.go │ ├── B1_RPC.go │ ├── B2_Method.go │ ├── B3_Message.go │ ├── C1_Stack.go │ ├── C2_Nodeinfo.go │ ├── C3_Service.go │ ├── C4_Full.go │ ├── D1_Protocols.go │ ├── D2_Multiservice.go │ ├── E1_Pss.go │ ├── E2_PssRouting.go │ ├── E3_PssSym.go │ ├── E4_PssRaw.go │ ├── E5_PssHandshake.go │ ├── E6_PssProtocol.go │ ├── E7_PssClient.go │ ├── F1_PssGoInit.go │ ├── F2_PssLow.go │ ├── LICENSE │ ├── README.md │ ├── common │ │ ├── common.go │ │ ├── defaults.go │ │ ├── keyfile │ │ └── protocol.go │ ├── doc │ │ ├── A1.latex │ │ ├── A2.latex │ │ ├── A3.latex │ │ ├── A4.latex │ │ ├── README.md │ │ ├── article.latex │ │ └── listings-golang.sty │ └── runall.sh └── protocol-complex │ ├── .service_old │ └── service.go │ ├── Dockerfile │ ├── README.md │ ├── bzz │ └── bzz.go │ ├── contracts │ └── pss.sol │ ├── main.go │ ├── main_pss.go │ ├── protocol │ ├── peer.go │ └── protocol.go │ ├── resource │ ├── README.md │ └── resource.go │ ├── service │ ├── api.go │ ├── minipow │ │ ├── check.go │ │ └── minipow.go │ ├── result.go │ ├── service.go │ ├── service_test.go │ ├── submit.go │ └── work.go │ ├── sim.go │ └── sim_pss.go ├── solidity ├── escrow │ ├── escrow.go │ ├── escrow.sol │ └── escrow_test.go └── upgrade │ ├── AbstractMain.sol │ ├── Main.sol │ ├── MainUpgrade.sol │ ├── README.md │ ├── Store.sol │ ├── Upgrader.sol │ ├── lib │ └── owned.sol │ └── remix.sh ├── swarm ├── bash │ ├── bzz.inc │ ├── bzz.sh │ ├── bzzp │ ├── bzzpaste.sh │ ├── bzzseed.sh │ └── bzztest.sh ├── docker │ ├── jaeger-test.sh │ ├── pss-ssl-test.sh │ └── pss-test.sh ├── mutable-resources │ ├── client-digest │ │ ├── mru.js │ │ ├── mrucli.js │ │ └── topic.js │ └── server-digest │ │ ├── digest.go │ │ └── signmru.go └── sqlite-vfs │ ├── LICENSE │ ├── README.md │ └── demo │ ├── .data.sql │ ├── Makefile │ ├── createdb.sh │ ├── hello │ ├── hello.c │ ├── hello.go │ └── hello.h │ ├── main_hello.c │ └── main_hello.go └── trezor ├── README.md ├── lib ├── ethereumjs │ ├── LICENSE.md │ └── ethereumjs-tx-1.3.3.min.js └── trezor │ ├── LICENCE.md │ └── trezor-connect.js ├── trezorsigneth.html └── trezorsignmsg.html /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ethereum 2 | 3 | A collection of code examples from the ethereum ecosystem, in any shape or size 4 | 5 | ### evmhacks 6 | 7 | Study behavior of `evm` directly from bytecode and assembly 8 | 9 | ### misc 10 | 11 | * **minipow**, a simple pow hasher implementation 12 | * **namehash**, a ens namehash cli command; compiled file way too big for its purpose, though. 13 | 14 | ### p2p 15 | 16 | * **devp2p**, a thorough code example tutorial of ethereum's node stack, `devp2p` protocol and `pss` messaging protocol. This was previously in a separate repo named `go-ethereum-p2p-demo`. 17 | * **protocol-complex**, an in-depth example of how to wrap a `devp2p` protocol to `pss` without changing the protocol code. 18 | 19 | ### solidity 20 | 21 | * **escrow**, WIP generic escrow contract 22 | * **upgrade**, example of upgradable contract 23 | 24 | ### swarm 25 | 26 | * **mutable resources**, a recursive retriever of mutable resource updates. Also includes a `js` updater used in a presentation for Swarm Orange Summit 2018. 27 | * **sqlite-vfs**, a poc `cgo` implementation for swarm as vfs backend for sqlite, read-only and minimal. 28 | -------------------------------------------------------------------------------- /evm/1_independentinterpreter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | //"bytes" 5 | "context" 6 | "fmt" 7 | "math" 8 | "math/big" 9 | "os" 10 | "time" 11 | 12 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core" 16 | "github.com/ethereum/go-ethereum/core/vm" 17 | "github.com/ethereum/go-ethereum/crypto" 18 | "github.com/ethereum/go-ethereum/log" 19 | ) 20 | 21 | var ( 22 | // bytecode is generated from this assembly code 23 | // (push value 10 (dec, int32, big endian) to offset 20 (dec) 24 | // store to memory 25 | // retrieve 1 byte at offset 20 + 32 - 1 26 | // return value (10) 27 | // main: 28 | // push 10 29 | // push 20 30 | // mstore 31 | // push 1 32 | // push 51 33 | // return 34 | //bytecode = common.FromHex("0x5b600a60145260016033f3") 35 | //bytecode = common.FromHex("5b600035801563000000145760205260206020f35b60017ff0000000000000000000000000000000000000000000000000000000000000001760005260206000f3") 36 | bytecode = common.FromHex("5b60003560005560005460005260046000f300") 37 | balance = big.NewInt(int64(math.Pow(2, 7))) 38 | ) 39 | 40 | func init() { 41 | //log.StreamHandler(os.Stderr, nil) 42 | h := log.LvlFilterHandler(log.LvlTrace, log.StdoutHandler) 43 | log.Root().SetHandler(h) 44 | } 45 | 46 | func main() { 47 | 48 | // create key and derive address 49 | privkey, err := crypto.GenerateKey() 50 | if err != nil { 51 | log.Error(err.Error()) 52 | os.Exit(1) 53 | } 54 | addr := crypto.PubkeyToAddress(privkey.PublicKey) 55 | 56 | // set up backend 57 | auth := bind.NewKeyedTransactor(privkey) 58 | alloc := make(core.GenesisAlloc, 1) 59 | alloc[auth.From] = core.GenesisAccount{ 60 | PrivateKey: crypto.FromECDSA(privkey), 61 | Balance: balance, 62 | } 63 | sim := backends.NewSimulatedBackend(alloc) 64 | 65 | // create the evm interpreter 66 | ctx := vm.Context{ 67 | CanTransfer: func(state vm.StateDB, addr common.Address, amount *big.Int) bool { 68 | return true 69 | }, 70 | Transfer: func(state vm.StateDB, laddr common.Address, raddr common.Address, amount *big.Int) { 71 | return 72 | }, 73 | GetHash: func(uint64) common.Hash { 74 | return common.StringToHash("foo") 75 | }, 76 | } 77 | evm := vm.NewEVM(ctx, sim.State(), sim.Config(), vm.Config{}) 78 | _ = vm.NewInterpreter(evm, vm.Config{}) 79 | 80 | // set up and run contract 81 | ct := vm.NewContract(vm.AccountRef(addr), vm.AccountRef(addr), big.NewInt(0), 2000000) 82 | ct.SetCallCode(&addr, crypto.Keccak256Hash(bytecode), bytecode) 83 | lctx, cancel := context.WithTimeout(context.Background(), time.Second) 84 | defer cancel() 85 | p, err := sim.PendingCodeAt(lctx, addr) 86 | if err != nil { 87 | fmt.Fprintf(os.Stderr, "error: %v", err.Error()) 88 | os.Exit(1) 89 | } 90 | //r, err := ipr.Run(0, ct, []byte{0xde, 0xad, 0xbe, 0xef}) 91 | r, g, err := evm.Call(vm.AccountRef(addr), ct.Address(), []byte{0xde, 0xad, 0xbe, 0xef}, 2000000, big.NewInt(0)) 92 | if err != nil { 93 | fmt.Fprintf(os.Stderr, "error: %v", err.Error()) 94 | os.Exit(1) 95 | } 96 | sim.Commit() 97 | 98 | fmt.Fprintf(os.Stdout, "return: %x\nleftovergas: %d\npending%d\n", r, g, p) 99 | // check result 100 | // if !bytes.Equal(r, []byte{0x0a}) { 101 | // fmt.Fprintf(os.Stderr, "expected [0x0a], got %v", r) 102 | // } 103 | } 104 | -------------------------------------------------------------------------------- /evm/2_deploybytecodetobackend.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "math/big" 8 | "os" 9 | "strings" 10 | 11 | "github.com/ethereum/go-ethereum" 12 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core" 16 | "github.com/ethereum/go-ethereum/core/types" 17 | "github.com/ethereum/go-ethereum/crypto" 18 | "github.com/ethereum/go-ethereum/log" 19 | ) 20 | 21 | var ( 22 | // compiled with evm cli, commit 43dd8e62 23 | 24 | // bytecode is generated from this assembly code 25 | // get input value 26 | // store it to first storage slot 27 | // load from storage slot to memory 28 | // return first 4 bytes from memory slot 29 | // (we will call this code with 0xdeadbeef = 4 bytes) 30 | // 31 | // push 0 32 | // calldataload 33 | // push 0 34 | // sstore 35 | // push 0 36 | // sload 37 | // push 0 38 | // mstore 39 | // push 4 40 | // push 0 41 | // return 42 | // 43 | prochex = "60003560005560005460005260046000f3" 44 | prochexlen = len(prochex) / 2 45 | 46 | // "deploy code": 47 | // copy bytes of code from offset 0x0c to memory and return 48 | // 49 | // push PROCHEXLEN 50 | // push 0 51 | // push PROCHEXLEN 52 | // push 0x0c 53 | // push 0 54 | // codecopy 55 | // return 56 | // 57 | deployhex = fmt.Sprintf("60%x600060%x600c600039f3", prochexlen, prochexlen) 58 | 59 | input = []byte{0xde, 0xad, 0xbe, 0xef} 60 | 61 | bytecode = common.FromHex(strings.Join([]string{deployhex, prochex}, "")) 62 | balance = big.NewInt(int64(1000000000000)) 63 | 64 | blocknumber = big.NewInt(0) 65 | ) 66 | 67 | type fakeBackend interface { 68 | bind.ContractBackend 69 | Commit() 70 | } 71 | 72 | func init() { 73 | //log.StreamHandler(os.Stderr, nil) 74 | //h := log.LvlFilterHandler(log.LvlTrace, log.StdoutHandler) 75 | //log.Root().SetHandler(h) 76 | } 77 | 78 | // increment block number when we "mine" 79 | func commit(sim fakeBackend) { 80 | sim.Commit() 81 | blocknumber.Add(blocknumber, big.NewInt(1)) 82 | } 83 | 84 | func main() { 85 | 86 | // create key and derive address 87 | privkeysend, err := crypto.GenerateKey() 88 | if err != nil { 89 | log.Error(err.Error()) 90 | os.Exit(1) 91 | } 92 | sendaddr := crypto.PubkeyToAddress(privkeysend.PublicKey) 93 | 94 | // set up backend and slip it some dineros 95 | auth := bind.NewKeyedTransactor(privkeysend) 96 | alloc := make(core.GenesisAlloc, 1) 97 | alloc[sendaddr] = core.GenesisAccount{ 98 | PrivateKey: crypto.FromECDSA(privkeysend), 99 | Balance: balance, 100 | } 101 | sim := backends.NewSimulatedBackend(alloc) 102 | 103 | // set some foo values for transactions 104 | nonce := uint64(0) 105 | gaslimit := big.NewInt(2000000) 106 | amount := big.NewInt(0) 107 | gasprice := big.NewInt(20) 108 | 109 | // Create the contract creation transaction 110 | // sign it and schedule it for execution 111 | rawTx := types.NewContractCreation(nonce, amount, gaslimit, gasprice, bytecode) 112 | if err != nil { 113 | log.Error(err.Error()) 114 | os.Exit(1) 115 | } 116 | signedTx, err := auth.Signer(types.HomesteadSigner{}, sendaddr, rawTx) 117 | if err != nil { 118 | log.Error(err.Error()) 119 | os.Exit(1) 120 | } 121 | txhash := signedTx.Hash() 122 | err = sim.SendTransaction(context.TODO(), signedTx) 123 | if err != nil { 124 | log.Error(err.Error()) 125 | os.Exit(1) 126 | } 127 | 128 | // Mine one block 129 | commit(sim) 130 | 131 | // get the receipt, which should contain the contract address 132 | rcpt, err := sim.TransactionReceipt(context.TODO(), txhash) 133 | if err != nil { 134 | log.Error(err.Error()) 135 | os.Exit(1) 136 | } 137 | contractaddress := rcpt.ContractAddress 138 | if len(contractaddress) == 0 { 139 | log.Error("empty contract address") 140 | os.Exit(1) 141 | } 142 | 143 | // retrieve the code from the contract address 144 | // verify that it matches the code we sent 145 | code, err := sim.CodeAt(context.TODO(), contractaddress, big.NewInt(1)) 146 | if err != nil { 147 | log.Error(err.Error()) 148 | os.Exit(1) 149 | } else if !bytes.Equal(code, common.FromHex(prochex)) { 150 | log.Error("Code at contract address does not match input bytecode", "code", fmt.Sprintf("%x", code)) 151 | os.Exit(1) 152 | } 153 | 154 | // call the runtime bytecode (not as transaction) 155 | // should not alter the storage, but should return 0xdeadbeef 156 | msg := ethereum.CallMsg{ 157 | From: sendaddr, 158 | To: &rcpt.ContractAddress, 159 | GasPrice: gasprice, 160 | Gas: gaslimit, 161 | Value: amount, 162 | Data: input, 163 | } 164 | r, err := sim.CallContract(context.TODO(), msg, blocknumber) 165 | if err != nil { 166 | log.Error(err.Error()) 167 | os.Exit(1) 168 | } else if !bytes.Equal(r, input) { 169 | log.Error("Expected return value deadbeef", "got", fmt.Sprintf("%x", r)) 170 | os.Exit(1) 171 | } 172 | storageat, err := sim.StorageAt(context.TODO(), contractaddress, common.BigToHash(big.NewInt(0)), blocknumber) 173 | if err != nil { 174 | log.Error(err.Error()) 175 | os.Exit(1) 176 | } else if !bytes.Equal(storageat, make([]byte, 32)) { 177 | log.Info("storageat should be empty", "contains", fmt.Sprintf("%x", storageat)) 178 | os.Exit(1) 179 | } 180 | 181 | // Create the transaction call, sign it and schedule 182 | nonce, err = sim.NonceAt(context.TODO(), contractaddress, blocknumber) 183 | if err != nil { 184 | log.Error(err.Error()) 185 | os.Exit(1) 186 | } 187 | rawTx = types.NewTransaction(nonce, contractaddress, amount, gaslimit, gasprice, input) 188 | signedTx, err = auth.Signer(types.HomesteadSigner{}, sendaddr, rawTx) 189 | if err != nil { 190 | log.Error(err.Error()) 191 | os.Exit(1) 192 | } 193 | txhash = signedTx.Hash() 194 | err = sim.SendTransaction(context.TODO(), signedTx) 195 | if err != nil { 196 | log.Error(err.Error()) 197 | os.Exit(1) 198 | } 199 | 200 | // mine another block 201 | commit(sim) 202 | 203 | // now storage should have been changed 204 | // index 0 should hold 0xdeadbeef 205 | storageat, err = sim.StorageAt(context.TODO(), contractaddress, common.BigToHash(big.NewInt(0)), blocknumber) 206 | if err != nil { 207 | log.Error(err.Error()) 208 | os.Exit(1) 209 | } 210 | 211 | // seeing is believing 212 | fmt.Printf("%x\n", storageat[:4]) 213 | } 214 | -------------------------------------------------------------------------------- /evm/README.md: -------------------------------------------------------------------------------- 1 | # evmhacks 2 | 3 | Lowlevel hack snippets into go-ethereum virtual machine 4 | -------------------------------------------------------------------------------- /misc/exportprivate/exportprivate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "os" 10 | "os/user" 11 | "strings" 12 | "syscall" 13 | 14 | "golang.org/x/crypto/ssh/terminal" 15 | 16 | "github.com/ethereum/go-ethereum/accounts/keystore" 17 | "github.com/ethereum/go-ethereum/common/hexutil" 18 | "github.com/ethereum/go-ethereum/crypto" 19 | "github.com/ethereum/go-ethereum/log" 20 | ) 21 | 22 | var ( 23 | g_dir string 24 | g_arg string 25 | g_file string 26 | g_help bool 27 | ) 28 | 29 | func main() { 30 | 31 | usr, err := user.Current() 32 | if err != nil { 33 | log.Error("Could not get user info to resolve homedir") 34 | os.Exit(1) 35 | } 36 | 37 | var debug bool 38 | defaultdatadir := fmt.Sprintf("%s/.ethereum", usr.HomeDir) 39 | flag.StringVar(&g_dir, "d", defaultdatadir, "datadir") 40 | flag.BoolVar(&g_help, "h", false, "show help") 41 | flag.BoolVar(&debug, "v", false, "show debug info") 42 | flag.Usage = func() { 43 | fmt.Println(` 44 | ****************************** 45 | * WARNING! WARNING! WARNING! * 46 | ****************************** 47 | 48 | this program will output your private key in hex format to the terminal. Your private key should be kept secret, at the risk of losing all funds within it, and possibly any accounts derived from it. 49 | 50 | Some recommended precautions: 51 | * Make sure noone is looking over your shoulder when you run this program. 52 | * Never store your private key on a digital device with insufficient encryption 53 | * Only use this tool on an airgapped machine 54 | 55 | The author of this application assumes no warranty or liability. 56 | 57 | ---- 58 | Usage: ethexport [flags] ") 59 | 60 | If argument is account hex, the keystore subdir of the datadir will be searched for a matching account 61 | If argument is keyfile, the -d flag will be ignored 62 | `) 63 | flag.PrintDefaults() 64 | } 65 | flag.Parse() 66 | 67 | if g_help { 68 | flag.Usage() 69 | os.Exit(0) 70 | } 71 | 72 | lvl := log.LvlError 73 | if debug { 74 | lvl = log.LvlDebug 75 | } 76 | h := log.LvlFilterHandler(lvl, log.StderrHandler) 77 | log.Root().SetHandler(h) 78 | 79 | if g_dir == "" { 80 | g_dir = defaultdatadir 81 | } 82 | 83 | if flag.Arg(0) == "" { 84 | log.Error("Account or keyfile must be specified") 85 | os.Exit(1) 86 | } 87 | g_arg = flag.Arg(0) 88 | 89 | // check if we have file or account 90 | var keyfile string 91 | if _, err := hexutil.Decode(g_arg); err != nil { 92 | log.Debug("input is keyfile") 93 | fi, err := os.Stat(g_arg) 94 | if err != nil { 95 | log.Error("Keyfile not found", "path", g_arg) 96 | os.Exit(1) 97 | } else if fi.IsDir() { 98 | log.Error("Keyfile argument is a directory", "path", g_arg) 99 | os.Exit(1) 100 | } 101 | keyfile = g_arg 102 | } else { 103 | log.Debug("input is account hex") 104 | fi, err := os.Stat(g_dir) 105 | if err != nil { 106 | log.Error("Keystore not found", "path", g_dir) 107 | os.Exit(1) 108 | } else if !fi.IsDir() { 109 | log.Error("Keystore is not a directory", "path", g_dir) 110 | os.Exit(1) 111 | } 112 | 113 | // search the directory for the key 114 | keystoredir := fmt.Sprintf("%s/keystore", g_dir) 115 | log.Debug("checking keystore dir", "dir", keystoredir) 116 | dircontents, err := ioutil.ReadDir(keystoredir) 117 | if err != nil { 118 | log.Error("Can't open keystore dir: %v", err) 119 | } 120 | for _, f := range dircontents { 121 | if strings.Contains(f.Name(), g_arg[2:]) { 122 | keyfile = fmt.Sprintf("%s/%s", keystoredir, f.Name()) 123 | } 124 | } 125 | } 126 | 127 | if keyfile == "" { 128 | log.Error("Account not found") 129 | os.Exit(1) 130 | } 131 | 132 | log.Info("opening account", "keyfile", keyfile) 133 | j, err := ioutil.ReadFile(keyfile) 134 | if err != nil { 135 | log.Error("cannot read file", "err", err) 136 | os.Exit(1) 137 | } 138 | bytePassword := make([]byte, 1024) 139 | stat, err := os.Stdin.Stat() 140 | if err != nil && err != io.EOF { 141 | log.Error("cannot access stdin", "err", err) 142 | os.Exit(1) 143 | } 144 | if (stat.Mode() & os.ModeCharDevice) == 0 { 145 | total := 0 146 | for { 147 | c, err := os.Stdin.Read(bytePassword) 148 | total += c 149 | log.Debug("read", "c", c) 150 | if err != nil { 151 | if err == io.EOF { 152 | if bytePassword[total-1] == 0x0a { 153 | total-- 154 | } 155 | bytePassword = bytePassword[:total] 156 | log.Debug("have", "pass", bytePassword) 157 | break 158 | } 159 | fmt.Fprintf(os.Stderr, "read err: %v", err) 160 | os.Exit(1) 161 | } 162 | } 163 | } else { 164 | fmt.Printf("pass:") 165 | bytePassword, err = terminal.ReadPassword(int(syscall.Stdin)) 166 | } 167 | passphrase := string(bytePassword) 168 | fmt.Println("\ndecrypting keyfile...") 169 | key, err := keystore.DecryptKey(j, passphrase) 170 | if err != nil { 171 | log.Error("key decrypt failed", "err", err) 172 | os.Exit(1) 173 | } 174 | 175 | privkeyhex := hex.EncodeToString(crypto.FromECDSA(key.PrivateKey)) 176 | log.Debug("priv", "hex", privkeyhex) 177 | privkeyregen, err := crypto.HexToECDSA(privkeyhex) 178 | if err != nil { 179 | log.Error("internal privkey conversion error", "err", err) 180 | os.Exit(1) 181 | } 182 | log.Info("ok", "privkey", privkeyhex, "address", crypto.PubkeyToAddress(privkeyregen.PublicKey)) 183 | fmt.Println(privkeyhex) 184 | } 185 | -------------------------------------------------------------------------------- /misc/minipow/minipow.go: -------------------------------------------------------------------------------- 1 | package minipow 2 | 3 | import ( 4 | "crypto/sha1" 5 | ) 6 | 7 | func Mine(data []byte, difficulty int, resultC chan<- []byte, quitC <-chan struct{}, debug func([]byte, []byte)) { 8 | h := sha1.New() 9 | 10 | datalen := len(data) 11 | hashsizeminusone := h.Size() - 1 12 | diffbytes := make([]byte, h.Size()) 13 | result := make([]byte, h.Size()) 14 | 15 | // generate the difficulty mask 16 | var register byte 17 | c := 0 18 | OUTER_ONE: 19 | for i := hashsizeminusone; i > 0; i-- { 20 | register = 0x01 21 | for j := 1; j < 256; j *= 2 { 22 | diffbytes[i] |= register 23 | register <<= 1 24 | c++ 25 | if c == difficulty { 26 | break OUTER_ONE 27 | } 28 | } 29 | } 30 | 31 | diffthreshold := hashsizeminusone - int(difficulty/8) 32 | 33 | // 256 bit number is pretty close to eternity 34 | OUTER_TWO: 35 | for { 36 | // timeout handling 37 | select { 38 | case <-quitC: 39 | break OUTER_TWO 40 | default: 41 | } 42 | 43 | // increment the nonce 44 | for i := datalen - 1; i >= 0; i-- { 45 | data[i]++ 46 | if data[i]|0x0 != 0 { 47 | break 48 | } 49 | } 50 | 51 | // hashhhhh 52 | h.Reset() 53 | h.Write(data) 54 | sum := h.Sum(nil) 55 | if debug != nil { 56 | debug(data, sum) 57 | } 58 | copy(result[:], sum) 59 | 60 | // and byte for byte with the difficulty mask 61 | // we only check the bytes we have to, though, until diffthreshold 62 | // if not 0, we failed miserably 63 | for i := hashsizeminusone; i >= diffthreshold; i-- { 64 | sum[i] &= diffbytes[i] 65 | if sum[i] != 0x0 { 66 | continue OUTER_TWO 67 | } 68 | } 69 | 70 | // there was rejoicing 71 | resultC <- result 72 | return 73 | } 74 | resultC <- nil 75 | } 76 | -------------------------------------------------------------------------------- /misc/minipow/minipow_test.go: -------------------------------------------------------------------------------- 1 | package minipow 2 | 3 | import ( 4 | "context" 5 | "crypto/rand" 6 | "encoding/binary" 7 | "flag" 8 | "fmt" 9 | "math" 10 | "os" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | const ( 16 | showlimit = 32 + 8 + 3 17 | ) 18 | 19 | var ( 20 | diff = flag.Int("d", 8, "difficulty") 21 | verbose = flag.Bool("vv", false, "verbose") 22 | datalen = flag.Int("l", 128, "data length (will be randomized") 23 | timeout = flag.Int("t", 10, "timeout in seconds") 24 | showoffset = 0 25 | showprefix = "" 26 | ) 27 | 28 | func init() { 29 | flag.Parse() 30 | if *datalen > showlimit { 31 | showoffset = *datalen - showlimit 32 | showprefix = "..." 33 | } 34 | } 35 | 36 | func debug(data []byte, sum []byte) { 37 | fmt.Printf("Trying %s%x ==> %x\n", showprefix, data[showoffset:], sum) 38 | } 39 | 40 | func TestMine(t *testing.T) { 41 | 42 | var debugFunc func([]byte, []byte) 43 | if *verbose { 44 | debugFunc = debug 45 | } 46 | 47 | fmt.Printf("Datalength %d, difficulty 2^%d = %d\n", *datalen, *diff, int(math.Pow(2, float64(*diff)))) 48 | 49 | // make up some data 50 | data := make([]byte, *datalen+8) 51 | rand.Read(data[:*datalen]) 52 | 53 | // start mining 54 | resultC := make(chan []byte) 55 | quitC := make(chan struct{}) 56 | go Mine(data, *diff, resultC, quitC, debugFunc) 57 | 58 | // set timeout, wait for return or cancel if it runs too long 59 | timeoutduration, err := time.ParseDuration(fmt.Sprintf("%ds", *timeout)) 60 | if err != nil { 61 | t.Fatalf("couldn't parse timeout '%d'", *timeout) 62 | } 63 | ctx, cancel := context.WithTimeout(context.Background(), timeoutduration) 64 | defer cancel() 65 | 66 | var result []byte 67 | select { 68 | case <-ctx.Done(): 69 | close(quitC) 70 | case result = <-resultC: 71 | } 72 | 73 | // output results 74 | rounds := binary.BigEndian.Uint64(data[len(data)-8:]) 75 | if result == nil { 76 | t.Fatalf("Timeout after %d rounds\n", rounds) 77 | os.Exit(1) 78 | } 79 | t.Logf("Found %x after %d rounds\n", result, rounds) 80 | } 81 | -------------------------------------------------------------------------------- /misc/namehash/namehash.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/ethereum/go-ethereum/contracts/ens" 8 | ) 9 | 10 | func main() { 11 | if len(os.Args) == 1 { 12 | return 13 | } 14 | fmt.Println(ens.EnsNode(os.Args[1]).Hex()) 15 | } 16 | -------------------------------------------------------------------------------- /misc/sign/offlinesigner.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "math/big" 8 | "os" 9 | "os/user" 10 | "strconv" 11 | "syscall" 12 | 13 | "golang.org/x/crypto/ssh/terminal" 14 | 15 | "github.com/ethereum/go-ethereum/accounts" 16 | "github.com/ethereum/go-ethereum/accounts/keystore" 17 | "github.com/ethereum/go-ethereum/common" 18 | "github.com/ethereum/go-ethereum/core/types" 19 | "github.com/ethereum/go-ethereum/log" 20 | ) 21 | 22 | const ( 23 | defaultGasLimit = 21000 24 | defaultGasPrice = 5 * 1000000000 // 40 gwei 25 | ethSzabo = float64(1000000) 26 | ) 27 | 28 | var ( 29 | g_amount uint64 30 | g_gasPrice uint64 31 | g_gasLimit uint64 32 | g_dir string 33 | g_nonce uint64 34 | g_from string 35 | g_to string 36 | ) 37 | 38 | func init() { 39 | 40 | var debug bool 41 | 42 | flag.StringVar(&g_from, "f", "", "Account to send transaction from") 43 | flag.StringVar(&g_to, "t", "", "Account to send transaction to") 44 | flag.StringVar(&g_dir, "d", "", "datadir") 45 | flag.Uint64Var(&g_nonce, "n", 0, "nonce") 46 | flag.BoolVar(&debug, "debug", false, "Output debug information") 47 | flag.Parse() 48 | 49 | lvl := log.LvlError 50 | if debug { 51 | lvl = log.LvlDebug 52 | } 53 | h := log.LvlFilterHandler(lvl, log.StderrHandler) 54 | log.Root().SetHandler(h) 55 | 56 | if g_from == "" { 57 | log.Error("Need from address") 58 | os.Exit(1) 59 | } 60 | if g_to == "" { 61 | log.Error("Need to-address") 62 | os.Exit(1) 63 | } 64 | amount, err := strconv.ParseFloat(flag.Arg(0), 32) 65 | if err != nil { 66 | log.Error("Invalid amount") 67 | os.Exit(1) 68 | } 69 | g_amount = uint64(amount * ethSzabo) 70 | 71 | if g_dir == "" { 72 | usr, err := user.Current() 73 | if err != nil { 74 | log.Error("Could not get user info to resolve homedir") 75 | os.Exit(1) 76 | } 77 | g_dir = fmt.Sprintf("%s/.ethereum/keystore", usr.HomeDir) 78 | } 79 | fi, err := os.Stat(g_dir) 80 | if err != nil { 81 | log.Error("datadir invalid", "reason", err) 82 | os.Exit(1) 83 | } else if !fi.IsDir() { 84 | log.Error("not a directory", "path", g_dir) 85 | os.Exit(1) 86 | } 87 | g_gasPrice = defaultGasPrice 88 | g_gasLimit = defaultGasLimit 89 | } 90 | 91 | func main() { 92 | 93 | store := keystore.NewKeyStore(g_dir, keystore.StandardScryptN, keystore.StandardScryptP) 94 | 95 | var from accounts.Account 96 | var wallet accounts.Wallet 97 | for _, w := range store.Wallets() { 98 | for _, a := range w.Accounts() { 99 | if a.Address == common.HexToAddress(g_from) { 100 | from = a 101 | wallet = w 102 | } 103 | } 104 | } 105 | 106 | zeroaccount := accounts.Account{} 107 | if from == zeroaccount { 108 | log.Error("From address not valid", "address", g_from) 109 | os.Exit(1) 110 | } 111 | 112 | to := common.HexToAddress(g_to) 113 | var wei big.Int 114 | szaboWei := big.NewInt(1000000000000) 115 | wei.Mul(big.NewInt(int64(g_amount)), szaboWei) 116 | //wei.Set(big.NewInt(1)) 117 | log.Debug("Creating transaction", "from", from, "to", to.Hex(), "amount", wei.Text(10), "nonce", g_nonce, "gaslimit", int64(g_gasLimit), "gasprice", int64(g_gasPrice)) 118 | tx := types.NewTransaction(g_nonce, to, &wei, big.NewInt(int64(g_gasLimit)), big.NewInt(int64(g_gasPrice)), []byte{}) 119 | 120 | fmt.Printf("pass: ") 121 | bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) 122 | passphrase := string(bytePassword) 123 | 124 | signedTx, err := wallet.SignTxWithPassphrase(from, passphrase, tx, nil) 125 | if err != nil { 126 | log.Error("Transaction hash sign fail", "reason", err) 127 | os.Exit(1) 128 | } 129 | rawTx := bytes.NewBuffer(nil) 130 | err = signedTx.EncodeRLP(rawTx) 131 | if err != nil { 132 | log.Error("Transaction RLP encode fail", "reason", err) 133 | os.Exit(1) 134 | } 135 | fmt.Printf("Tx: %x\nraw: %x\n", signedTx.Hash(), rawTx) 136 | } 137 | -------------------------------------------------------------------------------- /misc/sign/sign.go: -------------------------------------------------------------------------------- 1 | // signs arbitrary information from the command line 2 | package main 3 | 4 | import ( 5 | //"encoding/hex" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "os" 11 | "os/user" 12 | "strings" 13 | "syscall" 14 | 15 | "golang.org/x/crypto/ssh/terminal" 16 | 17 | "github.com/ethereum/go-ethereum/accounts/keystore" 18 | "github.com/ethereum/go-ethereum/common/hexutil" 19 | //"github.com/ethereum/go-ethereum/crypto" 20 | "github.com/ethereum/go-ethereum/common" 21 | "github.com/ethereum/go-ethereum/log" 22 | // "github.com/ethereum/go-ethereum/swarm/storage" 23 | "github.com/ethereum/go-ethereum/swarm/storage/mru" 24 | ) 25 | 26 | var ( 27 | g_dir string 28 | g_arg string 29 | g_file string 30 | g_help bool 31 | ) 32 | 33 | func main() { 34 | 35 | usr, err := user.Current() 36 | if err != nil { 37 | log.Error("Could not get user info to resolve homedir") 38 | os.Exit(1) 39 | } 40 | 41 | var debug bool 42 | defaultdatadir := fmt.Sprintf("%s/.ethereum", usr.HomeDir) 43 | flag.StringVar(&g_dir, "d", defaultdatadir, "datadir") 44 | flag.BoolVar(&g_help, "h", false, "show help") 45 | flag.BoolVar(&debug, "v", false, "show debug info") 46 | flag.Usage = func() { 47 | flag.PrintDefaults() 48 | } 49 | flag.Parse() 50 | 51 | if g_help { 52 | flag.Usage() 53 | os.Exit(0) 54 | } 55 | 56 | lvl := log.LvlError 57 | if debug { 58 | lvl = log.LvlDebug 59 | } 60 | h := log.LvlFilterHandler(lvl, log.StderrHandler) 61 | log.Root().SetHandler(h) 62 | 63 | if g_dir == "" { 64 | g_dir = defaultdatadir 65 | } 66 | 67 | if flag.Arg(0) == "" { 68 | log.Error("Account or keyfile must be specified") 69 | os.Exit(1) 70 | } 71 | g_arg = flag.Arg(0) 72 | 73 | if flag.Arg(1) == "" { 74 | log.Error("Specify file to sign") 75 | os.Exit(1) 76 | } 77 | g_file = flag.Arg(1) 78 | 79 | // check if we have file or account 80 | var keyfile string 81 | if _, err := hexutil.Decode(g_arg); err != nil { 82 | log.Debug("input is keyfile") 83 | fi, err := os.Stat(g_arg) 84 | if err != nil { 85 | log.Error("Keyfile not found", "path", g_arg) 86 | os.Exit(1) 87 | } else if fi.IsDir() { 88 | log.Error("Keyfile argument is a directory", "path", g_arg) 89 | os.Exit(1) 90 | } 91 | keyfile = g_arg 92 | } else { 93 | log.Debug("input is account hex") 94 | fi, err := os.Stat(g_dir) 95 | if err != nil { 96 | log.Error("Keystore not found", "path", g_dir) 97 | os.Exit(1) 98 | } else if !fi.IsDir() { 99 | log.Error("Keystore is not a directory", "path", g_dir) 100 | os.Exit(1) 101 | } 102 | 103 | // search the directory for the key 104 | keystoredir := fmt.Sprintf("%s/keystore", g_dir) 105 | log.Debug("checking keystore dir", "dir", keystoredir) 106 | dircontents, err := ioutil.ReadDir(keystoredir) 107 | if err != nil { 108 | log.Error("Can't open keystore dir: %v", err) 109 | } 110 | for _, f := range dircontents { 111 | if strings.Contains(f.Name(), g_arg[2:]) { 112 | keyfile = fmt.Sprintf("%s/%s", keystoredir, f.Name()) 113 | } 114 | } 115 | } 116 | 117 | if keyfile == "" { 118 | log.Error("Account not found") 119 | os.Exit(1) 120 | } 121 | 122 | log.Info("opening account", "keyfile", keyfile) 123 | j, err := ioutil.ReadFile(keyfile) 124 | if err != nil { 125 | log.Error("cannot read file", "err", err) 126 | os.Exit(1) 127 | } 128 | bytePassword := make([]byte, 1024) 129 | stat, err := os.Stdin.Stat() 130 | if err != nil && err != io.EOF { 131 | log.Error("cannot access stdin", "err", err) 132 | os.Exit(1) 133 | } 134 | if (stat.Mode() & os.ModeCharDevice) == 0 { 135 | total := 0 136 | for { 137 | c, err := os.Stdin.Read(bytePassword) 138 | total += c 139 | log.Debug("read", "c", c) 140 | if err != nil { 141 | if err == io.EOF { 142 | if bytePassword[total-1] == 0x0a { 143 | total-- 144 | } 145 | bytePassword = bytePassword[:total] 146 | log.Debug("have", "pass", bytePassword) 147 | break 148 | } 149 | fmt.Fprintf(os.Stderr, "read err: %v", err) 150 | os.Exit(1) 151 | } 152 | } 153 | } else { 154 | fmt.Printf("pass:") 155 | bytePassword, err = terminal.ReadPassword(int(syscall.Stdin)) 156 | } 157 | passphrase := string(bytePassword) 158 | fmt.Println("\ndecrypting keyfile...") 159 | key, err := keystore.DecryptKey(j, passphrase) 160 | if err != nil { 161 | log.Error("key decrypt failed", "err", err) 162 | os.Exit(1) 163 | } 164 | 165 | content, err := ioutil.ReadFile(g_file) 166 | if err != nil { 167 | log.Error("read data to sign fail", "err", err) 168 | os.Exit(1) 169 | } 170 | 171 | contentBytes, err := hexutil.Decode(string(content)) 172 | if err != nil { 173 | log.Error("failed to convert content to bytes", "err", err) 174 | os.Exit(1) 175 | } 176 | 177 | signer := mru.NewGenericSigner(key.PrivateKey) 178 | 179 | // hsh := storage.MakeHashFunc(storage.SHA3Hash)() 180 | // hsh.Reset() 181 | // hsh.Write(contentBytes) 182 | 183 | updateSignature, err := signer.Sign(common.BytesToHash(contentBytes)) 184 | if err != nil { 185 | log.Error("sign fail", "err", err) 186 | os.Exit(1) 187 | } 188 | 189 | fmt.Println(hexutil.Encode(updateSignature[:])) 190 | } 191 | -------------------------------------------------------------------------------- /misc/sign/txsign.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "math/big" 9 | "os" 10 | "os/user" 11 | "strconv" 12 | "syscall" 13 | 14 | "golang.org/x/crypto/ssh/terminal" 15 | 16 | "github.com/ethereum/go-ethereum/accounts" 17 | "github.com/ethereum/go-ethereum/accounts/keystore" 18 | "github.com/ethereum/go-ethereum/common" 19 | "github.com/ethereum/go-ethereum/core/types" 20 | "github.com/ethereum/go-ethereum/log" 21 | ) 22 | 23 | const ( 24 | defaultGasLimit = 21000 25 | defaultGasPrice = 5 * 1000000000 // 40 gwei 26 | ethSzabo = float64(1000000) 27 | ) 28 | 29 | var ( 30 | g_amount uint64 31 | g_gasPrice uint64 32 | g_gasLimit uint64 33 | g_dir string 34 | g_nonce uint64 35 | g_from string 36 | g_to string 37 | g_tx bool 38 | ) 39 | 40 | func init() { 41 | 42 | var debug bool 43 | 44 | flag.StringVar(&g_from, "f", "", "Account to send transaction from") 45 | flag.StringVar(&g_to, "t", "", "Account to send transaction to") 46 | flag.StringVar(&g_dir, "d", "", "datadir") 47 | flag.Uint64Var(&g_nonce, "n", 0, "nonce") 48 | flag.BoolVar(&g_tx, "x", false, "include tx hash in output") 49 | flag.BoolVar(&debug, "v", false, "Output debug information") 50 | flag.Parse() 51 | 52 | lvl := log.LvlError 53 | if debug { 54 | lvl = log.LvlDebug 55 | } 56 | h := log.LvlFilterHandler(lvl, log.StderrHandler) 57 | log.Root().SetHandler(h) 58 | 59 | if g_from == "" { 60 | log.Error("Need from address") 61 | os.Exit(1) 62 | } 63 | if g_to == "" { 64 | log.Error("Need to-address") 65 | os.Exit(1) 66 | } 67 | amount, err := strconv.ParseFloat(flag.Arg(0), 32) 68 | if err != nil { 69 | log.Error("Invalid amount") 70 | os.Exit(1) 71 | } 72 | g_amount = uint64(amount * ethSzabo) 73 | 74 | if g_dir == "" { 75 | usr, err := user.Current() 76 | if err != nil { 77 | log.Error("Could not get user info to resolve homedir") 78 | os.Exit(1) 79 | } 80 | g_dir = fmt.Sprintf("%s/.ethereum/keystore", usr.HomeDir) 81 | } 82 | fi, err := os.Stat(g_dir) 83 | if err != nil { 84 | log.Error("datadir invalid", "reason", err) 85 | os.Exit(1) 86 | } else if !fi.IsDir() { 87 | log.Error("not a directory", "path", g_dir) 88 | os.Exit(1) 89 | } 90 | g_gasPrice = defaultGasPrice 91 | g_gasLimit = defaultGasLimit 92 | } 93 | 94 | func main() { 95 | 96 | store := keystore.NewKeyStore(g_dir, keystore.StandardScryptN, keystore.StandardScryptP) 97 | 98 | var from accounts.Account 99 | var wallet accounts.Wallet 100 | for _, w := range store.Wallets() { 101 | for _, a := range w.Accounts() { 102 | if a.Address == common.HexToAddress(g_from) { 103 | from = a 104 | wallet = w 105 | } 106 | } 107 | } 108 | 109 | zeroaccount := accounts.Account{} 110 | if from == zeroaccount { 111 | log.Error("From address not valid", "address", g_from) 112 | os.Exit(1) 113 | } 114 | 115 | to := common.HexToAddress(g_to) 116 | var wei big.Int 117 | szaboWei := big.NewInt(1000000000000) 118 | wei.Mul(big.NewInt(int64(g_amount)), szaboWei) 119 | //wei.Set(big.NewInt(1)) 120 | log.Debug("Creating transaction", "from", from, "to", to.Hex(), "amount", wei.Text(10), "nonce", g_nonce, "gaslimit", int64(g_gasLimit), "gasprice", int64(g_gasPrice)) 121 | tx := types.NewTransaction(g_nonce, to, &wei, uint64(g_gasLimit), big.NewInt(int64(g_gasPrice)), []byte{}) 122 | 123 | bytePassword := make([]byte, 1024) 124 | stat, err := os.Stdin.Stat() 125 | if err != nil && err != io.EOF { 126 | log.Error("cannot access stdin", "err", err) 127 | os.Exit(1) 128 | } 129 | if (stat.Mode() & os.ModeCharDevice) == 0 { 130 | total := 0 131 | for { 132 | c, err := os.Stdin.Read(bytePassword) 133 | total += c 134 | log.Debug("read", "c", c) 135 | if err != nil { 136 | if err == io.EOF { 137 | if bytePassword[total-1] == 0x0a { 138 | total-- 139 | } 140 | bytePassword = bytePassword[:total] 141 | break 142 | } 143 | fmt.Fprintf(os.Stderr, "read err: %v", err) 144 | os.Exit(1) 145 | } 146 | } 147 | } else { 148 | fmt.Printf("pass:") 149 | bytePassword, err = terminal.ReadPassword(int(syscall.Stdin)) 150 | } 151 | passphrase := string(bytePassword) 152 | 153 | signedTx, err := wallet.SignTxWithPassphrase(from, passphrase, tx, nil) 154 | if err != nil { 155 | log.Error("Transaction hash sign fail", "reason", err) 156 | os.Exit(1) 157 | } 158 | rawTx := bytes.NewBuffer(nil) 159 | err = signedTx.EncodeRLP(rawTx) 160 | if err != nil { 161 | log.Error("Transaction RLP encode fail", "reason", err) 162 | os.Exit(1) 163 | } 164 | if g_tx { 165 | fmt.Printf("Tx: %x\nraw: %x\n", signedTx.Hash(), rawTx) 166 | } else { 167 | fmt.Printf("%x\n", rawTx) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /p2p/devp2p/A1_Server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/crypto" 6 | "github.com/ethereum/go-ethereum/p2p" 7 | 8 | demo "./common" 9 | ) 10 | 11 | func main() { 12 | 13 | // make a new private key 14 | privkey, err := crypto.GenerateKey() 15 | if err != nil { 16 | demo.Log.Crit("Generate private key failed", "err", err) 17 | } 18 | 19 | // set up server 20 | cfg := p2p.Config{ 21 | PrivateKey: privkey, 22 | Name: common.MakeName("foo", "42"), 23 | } 24 | srv := p2p.Server{ 25 | Config: cfg, 26 | } 27 | 28 | // attempt to start the server 29 | err = srv.Start() 30 | if err != nil { 31 | demo.Log.Crit("Start p2p.Server failed", "err", err) 32 | } 33 | 34 | // inspect the resulting values 35 | nodeinfo := srv.NodeInfo() 36 | demo.Log.Info("server started", "enode", nodeinfo.Enode, "name", nodeinfo.Name, "ID", nodeinfo.ID, "IP", nodeinfo.IP) 37 | 38 | // bring down the server 39 | srv.Stop() 40 | } 41 | -------------------------------------------------------------------------------- /p2p/devp2p/A2_Connect.go: -------------------------------------------------------------------------------- 1 | // bring up two nodes and connect them 2 | package main 3 | 4 | import ( 5 | "crypto/ecdsa" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/crypto" 11 | "github.com/ethereum/go-ethereum/p2p" 12 | 13 | demo "./common" 14 | ) 15 | 16 | // create a server 17 | func newServer(privkey *ecdsa.PrivateKey, name string, version string, port int) *p2p.Server { 18 | 19 | // we need to explicitly allow at least one peer 20 | // otherwise the connection attempt will be refused 21 | cfg := p2p.Config{ 22 | PrivateKey: privkey, 23 | Name: common.MakeName(name, version), 24 | MaxPeers: 1, 25 | } 26 | if port > 0 { 27 | cfg.ListenAddr = fmt.Sprintf(":%d", port) 28 | } 29 | srv := &p2p.Server{ 30 | Config: cfg, 31 | } 32 | return srv 33 | } 34 | 35 | func main() { 36 | 37 | // we need private keys for both servers 38 | privkey_one, err := crypto.GenerateKey() 39 | if err != nil { 40 | demo.Log.Crit("Generate private key #1 failed", "err", err) 41 | } 42 | privkey_two, err := crypto.GenerateKey() 43 | if err != nil { 44 | demo.Log.Crit("Generate private key #2 failed", "err", err) 45 | } 46 | 47 | // set up the two servers 48 | srv_one := newServer(privkey_one, "foo", "42", 0) 49 | err = srv_one.Start() 50 | if err != nil { 51 | demo.Log.Crit("Start p2p.Server #1 failed", "err", err) 52 | } 53 | 54 | srv_two := newServer(privkey_two, "bar", "666", 31234) 55 | err = srv_two.Start() 56 | if err != nil { 57 | demo.Log.Crit("Start p2p.Server #2 failed", "err", err) 58 | } 59 | 60 | // get the node instance of the second server 61 | node_two := srv_two.Self() 62 | 63 | // add it as a peer to the first node 64 | // the connection and crypto handshake will be performed automatically 65 | srv_one.AddPeer(node_two) 66 | 67 | // wait for the connection to complete 68 | time.Sleep(time.Millisecond * 100) 69 | 70 | // inspect the results 71 | demo.Log.Info("after add", "node one peers", srv_one.Peers(), "node two peers", srv_two.Peers()) 72 | 73 | // stop the servers 74 | srv_one.Stop() 75 | srv_two.Stop() 76 | } 77 | -------------------------------------------------------------------------------- /p2p/devp2p/A3_Events.go: -------------------------------------------------------------------------------- 1 | // get notified when the peer connection has been completed 2 | package main 3 | 4 | import ( 5 | "crypto/ecdsa" 6 | "fmt" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/crypto" 10 | "github.com/ethereum/go-ethereum/p2p" 11 | 12 | demo "./common" 13 | ) 14 | 15 | var ( 16 | quitC = make(chan bool) 17 | ) 18 | 19 | // create a server 20 | func newServer(privkey *ecdsa.PrivateKey, name string, version string, port int) *p2p.Server { 21 | 22 | // we need to explicitly allow at least one peer, otherwise the connection attempt will be refused 23 | cfg := p2p.Config{ 24 | PrivateKey: privkey, 25 | Name: common.MakeName(name, version), 26 | MaxPeers: 1, 27 | } 28 | if port > 0 { 29 | cfg.ListenAddr = fmt.Sprintf(":%d", port) 30 | } 31 | srv := &p2p.Server{ 32 | Config: cfg, 33 | } 34 | return srv 35 | } 36 | 37 | func main() { 38 | 39 | // we need private keys for both servers 40 | privkey_one, err := crypto.GenerateKey() 41 | if err != nil { 42 | demo.Log.Crit("Generate private key #1 failed", "err", err) 43 | } 44 | privkey_two, err := crypto.GenerateKey() 45 | if err != nil { 46 | demo.Log.Crit("Generate private key #2 failed", "err", err) 47 | } 48 | 49 | // set up the two servers 50 | srv_one := newServer(privkey_one, "foo", "42", 0) 51 | err = srv_one.Start() 52 | if err != nil { 53 | demo.Log.Crit("Start p2p.Server #1 failed", "err", err) 54 | } 55 | 56 | srv_two := newServer(privkey_two, "bar", "666", 31234) 57 | err = srv_two.Start() 58 | if err != nil { 59 | demo.Log.Crit("Start p2p.Server #2 failed", "err", err) 60 | } 61 | 62 | // set up the event subscription on the first server 63 | eventC := make(chan *p2p.PeerEvent) 64 | sub_one := srv_one.SubscribeEvents(eventC) 65 | 66 | // listen for events 67 | go func() { 68 | peerevent := <-eventC 69 | demo.Log.Info("received peerevent", "type", peerevent.Type, "peer", peerevent.Peer) 70 | quitC <- true 71 | }() 72 | 73 | // get the node instance of the second server 74 | node_two := srv_two.Self() 75 | 76 | // add it as a peer to the first node 77 | // the connection and crypto handshake will be performed automatically 78 | srv_one.AddPeer(node_two) 79 | 80 | // receives when the event is received 81 | <-quitC 82 | 83 | // inspect the results 84 | demo.Log.Info("after add", "node one peers", srv_one.Peers(), "node two peers", srv_two.Peers()) 85 | 86 | // terminate subscription 87 | sub_one.Unsubscribe() 88 | 89 | // stop the servers 90 | srv_one.Stop() 91 | srv_two.Stop() 92 | } 93 | -------------------------------------------------------------------------------- /p2p/devp2p/A4_Message.go: -------------------------------------------------------------------------------- 1 | // send, receive, get notified about a message 2 | package main 3 | 4 | import ( 5 | "crypto/ecdsa" 6 | "fmt" 7 | "sync" 8 | 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/crypto" 11 | "github.com/ethereum/go-ethereum/p2p" 12 | 13 | demo "./common" 14 | ) 15 | 16 | var ( 17 | messageW = &sync.WaitGroup{} 18 | ) 19 | 20 | type FooMsg struct { 21 | V uint 22 | } 23 | 24 | // create a protocol that can take care of message sending 25 | // the Run function is invoked upon connection 26 | // it gets passed: 27 | // * an instance of p2p.Peer, which represents the remote peer 28 | // * an instance of p2p.MsgReadWriter, which is the io between the node and its peer 29 | var ( 30 | proto = p2p.Protocol{ 31 | Name: "foo", 32 | Version: 42, 33 | Length: 1, 34 | Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 35 | 36 | // simplest payload possible; a byte slice 37 | outmsg := "foobar" 38 | 39 | // send the message 40 | err := p2p.Send(rw, 0, outmsg) 41 | if err != nil { 42 | return fmt.Errorf("Send p2p message fail: %v", err) 43 | } 44 | demo.Log.Info("sending message", "peer", p, "msg", outmsg) 45 | 46 | // wait for the message to come in from the other side 47 | // note that receive message event doesn't get emitted until we ReadMsg() 48 | inmsg, err := rw.ReadMsg() 49 | if err != nil { 50 | return fmt.Errorf("Receive p2p message fail: %v", err) 51 | } 52 | demo.Log.Info("received message", "peer", p, "msg", inmsg) 53 | 54 | // terminate the protocol 55 | return nil 56 | }, 57 | } 58 | ) 59 | 60 | // create a server 61 | func newServer(privkey *ecdsa.PrivateKey, name string, version string, port int) *p2p.Server { 62 | 63 | // we need to explicitly allow at least one peer, otherwise the connection attempt will be refused 64 | // we also need to explicitly tell the server to generate events for messages 65 | cfg := p2p.Config{ 66 | PrivateKey: privkey, 67 | Name: common.MakeName(name, version), 68 | MaxPeers: 1, 69 | Protocols: []p2p.Protocol{proto}, 70 | EnableMsgEvents: true, 71 | } 72 | if port > 0 { 73 | cfg.ListenAddr = fmt.Sprintf(":%d", port) 74 | } 75 | srv := &p2p.Server{ 76 | Config: cfg, 77 | } 78 | return srv 79 | } 80 | 81 | func main() { 82 | 83 | // we need private keys for both servers 84 | privkey_one, err := crypto.GenerateKey() 85 | if err != nil { 86 | demo.Log.Crit("Generate private key #1 failed", "err", err) 87 | } 88 | privkey_two, err := crypto.GenerateKey() 89 | if err != nil { 90 | demo.Log.Crit("Generate private key #2 failed", "err", err) 91 | } 92 | 93 | // set up the two servers 94 | srv_one := newServer(privkey_one, "foo", "42", 0) 95 | err = srv_one.Start() 96 | if err != nil { 97 | demo.Log.Crit("Start p2p.Server #1 failed", "err", err) 98 | } 99 | 100 | srv_two := newServer(privkey_two, "bar", "666", 31234) 101 | err = srv_two.Start() 102 | if err != nil { 103 | demo.Log.Crit("Start p2p.Server #2 failed", "err", err) 104 | } 105 | 106 | // set up the event subscriptions on both servers 107 | // the Err() on the Subscription object returns when subscription is closed 108 | eventOneC := make(chan *p2p.PeerEvent) 109 | sub_one := srv_one.SubscribeEvents(eventOneC) 110 | messageW.Add(1) 111 | go func() { 112 | for { 113 | peerevent := <-eventOneC 114 | if peerevent.Type == "add" { 115 | demo.Log.Debug("Received peer add notification on node #1", "peer", peerevent.Peer) 116 | } else if peerevent.Type == "msgrecv" { 117 | demo.Log.Info("Received message nofification on node #1", "event", peerevent) 118 | 119 | messageW.Done() 120 | return 121 | } 122 | } 123 | }() 124 | 125 | eventTwoC := make(chan *p2p.PeerEvent) 126 | sub_two := srv_two.SubscribeEvents(eventTwoC) 127 | messageW.Add(1) 128 | go func() { 129 | for { 130 | peerevent := <-eventTwoC 131 | if peerevent.Type == "add" { 132 | demo.Log.Debug("Received peer add notification on node #2", "peer", peerevent.Peer) 133 | } else if peerevent.Type == "msgrecv" { 134 | demo.Log.Info("Received message nofification on node #2", "event", peerevent) 135 | messageW.Done() 136 | return 137 | } 138 | } 139 | }() 140 | 141 | // get the node instance of the second server 142 | node_two := srv_two.Self() 143 | 144 | // add it as a peer to the first node 145 | // the connection and crypto handshake will be performed automatically 146 | srv_one.AddPeer(node_two) 147 | 148 | // wait for each respective message to be delivered on both sides 149 | messageW.Wait() 150 | 151 | // terminate subscription loops and unsubscribe 152 | sub_one.Unsubscribe() 153 | sub_two.Unsubscribe() 154 | 155 | // stop the servers 156 | srv_one.Stop() 157 | srv_two.Stop() 158 | } 159 | -------------------------------------------------------------------------------- /p2p/devp2p/A5_Reply.go: -------------------------------------------------------------------------------- 1 | // send, receive, get notified about a message 2 | package main 3 | 4 | import ( 5 | "crypto/ecdsa" 6 | "fmt" 7 | "sync" 8 | "time" 9 | 10 | "github.com/ethereum/go-ethereum/common" 11 | "github.com/ethereum/go-ethereum/crypto" 12 | "github.com/ethereum/go-ethereum/p2p" 13 | 14 | demo "./common" 15 | ) 16 | 17 | var ( 18 | protoW = &sync.WaitGroup{} 19 | pingW = &sync.WaitGroup{} 20 | ) 21 | 22 | type FooPingMsg struct { 23 | Pong bool 24 | Created time.Time 25 | } 26 | 27 | // create a protocol that can take care of message sending 28 | // the Run function is invoked upon connection 29 | // it gets passed: 30 | // * an instance of p2p.Peer, which represents the remote peer 31 | // * an instance of p2p.MsgReadWriter, which is the io between the node and its peer 32 | var ( 33 | proto = p2p.Protocol{ 34 | Name: "foo", 35 | Version: 42, 36 | Length: 1, 37 | Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 38 | 39 | pingW.Add(1) 40 | ponged := false 41 | 42 | // create the message structure 43 | msg := FooPingMsg{ 44 | Pong: false, 45 | Created: time.Now(), 46 | } 47 | 48 | // send the message 49 | err := p2p.Send(rw, 0, msg) 50 | if err != nil { 51 | return fmt.Errorf("Send p2p message fail: %v", err) 52 | } 53 | demo.Log.Info("sending ping", "peer", p) 54 | 55 | for !ponged { 56 | // wait for the message to come in from the other side 57 | // note that receive message event doesn't get emitted until we ReadMsg() 58 | msg, err := rw.ReadMsg() 59 | if err != nil { 60 | return fmt.Errorf("Receive p2p message fail: %v", err) 61 | } 62 | 63 | // decode the message and check the contents 64 | var decodedmsg FooPingMsg 65 | err = msg.Decode(&decodedmsg) 66 | if err != nil { 67 | return fmt.Errorf("Decode p2p message fail: %v", err) 68 | } 69 | 70 | if decodedmsg.Pong { 71 | demo.Log.Info("received pong", "peer", p) 72 | ponged = true 73 | pingW.Done() 74 | } else { 75 | demo.Log.Info("received ping", "peer", p) 76 | msg := FooPingMsg{ 77 | Pong: true, 78 | Created: time.Now(), 79 | } 80 | err := p2p.Send(rw, 0, msg) 81 | if err != nil { 82 | return fmt.Errorf("Send p2p message fail: %v", err) 83 | } 84 | demo.Log.Info("sent pong", "peer", p) 85 | } 86 | 87 | } 88 | 89 | // terminate the protocol after all involved have completed the cycle 90 | pingW.Wait() 91 | protoW.Done() 92 | return nil 93 | }, 94 | } 95 | ) 96 | 97 | // create a server 98 | func newServer(privkey *ecdsa.PrivateKey, name string, version string, port int) *p2p.Server { 99 | 100 | // we need to explicitly allow at least one peer, otherwise the connection attempt will be refused 101 | // we also need to explicitly tell the server to generate events for messages 102 | cfg := p2p.Config{ 103 | PrivateKey: privkey, 104 | Name: common.MakeName(name, version), 105 | MaxPeers: 1, 106 | Protocols: []p2p.Protocol{proto}, 107 | EnableMsgEvents: true, 108 | } 109 | if port > 0 { 110 | cfg.ListenAddr = fmt.Sprintf(":%d", port) 111 | } 112 | srv := &p2p.Server{ 113 | Config: cfg, 114 | } 115 | return srv 116 | } 117 | 118 | func main() { 119 | 120 | // we need private keys for both servers 121 | privkey_one, err := crypto.GenerateKey() 122 | if err != nil { 123 | demo.Log.Crit("Generate private key #1 failed", "err", err) 124 | } 125 | privkey_two, err := crypto.GenerateKey() 126 | if err != nil { 127 | demo.Log.Crit("Generate private key #2 failed", "err", err) 128 | } 129 | 130 | // set up the two servers 131 | srv_one := newServer(privkey_one, "foo", "42", 0) 132 | err = srv_one.Start() 133 | if err != nil { 134 | demo.Log.Crit("Start p2p.Server #1 failed", "err", err) 135 | } 136 | 137 | srv_two := newServer(privkey_two, "bar", "666", 31234) 138 | err = srv_two.Start() 139 | if err != nil { 140 | demo.Log.Crit("Start p2p.Server #2 failed", "err", err) 141 | } 142 | 143 | // set up the event subscriptions on both servers 144 | // the Err() on the Subscription object returns when subscription is closed 145 | eventOneC := make(chan *p2p.PeerEvent) 146 | sub_one := srv_one.SubscribeEvents(eventOneC) 147 | protoW.Add(1) 148 | go func() { 149 | for { 150 | select { 151 | case peerevent := <-eventOneC: 152 | if peerevent.Type == "add" { 153 | demo.Log.Debug("Received peer add notification on node #1", "peer", peerevent.Peer) 154 | } else if peerevent.Type == "msgrecv" { 155 | demo.Log.Info("Received message nofification on node #1", "event", peerevent) 156 | } 157 | case <-sub_one.Err(): 158 | return 159 | } 160 | } 161 | }() 162 | 163 | eventTwoC := make(chan *p2p.PeerEvent) 164 | sub_two := srv_two.SubscribeEvents(eventTwoC) 165 | protoW.Add(1) 166 | go func() { 167 | for { 168 | select { 169 | case peerevent := <-eventTwoC: 170 | if peerevent.Type == "add" { 171 | demo.Log.Debug("Received peer add notification on node #2", "peer", peerevent.Peer) 172 | } else if peerevent.Type == "msgrecv" { 173 | demo.Log.Info("Received message nofification on node #2", "event", peerevent) 174 | } 175 | case <-sub_two.Err(): 176 | return 177 | } 178 | } 179 | }() 180 | 181 | // get the node instance of the second server 182 | node_two := srv_two.Self() 183 | 184 | // add it as a peer to the first node 185 | // the connection and crypto handshake will be performed automatically 186 | srv_one.AddPeer(node_two) 187 | 188 | // wait for each respective message to be delivered on both sides 189 | protoW.Wait() 190 | 191 | // terminate subscription loops and unsubscribe 192 | sub_one.Unsubscribe() 193 | sub_two.Unsubscribe() 194 | 195 | // stop the servers 196 | srv_one.Stop() 197 | srv_two.Stop() 198 | } 199 | -------------------------------------------------------------------------------- /p2p/devp2p/B1_RPC.go: -------------------------------------------------------------------------------- 1 | // RPC hello world 2 | package main 3 | 4 | import ( 5 | "net" 6 | "os" 7 | 8 | "github.com/ethereum/go-ethereum/rpc" 9 | 10 | demo "./common" 11 | ) 12 | 13 | // set up an object that can contain the API methods 14 | type FooAPI struct { 15 | } 16 | 17 | // a valid API method is exported, has a pointer receiver and returns error as last argument 18 | // the method will be called with _helloWorld 19 | // (first letter in method is lowercase, module name and method name separated by underscore) 20 | func (api *FooAPI) HelloWorld() (string, error) { 21 | return "foobar", nil 22 | } 23 | 24 | func main() { 25 | 26 | // set up the RPC server 27 | rpcsrv := rpc.NewServer() 28 | err := rpcsrv.RegisterName("foo", &FooAPI{}) 29 | if err != nil { 30 | demo.Log.Crit("Register API method(s) fail", "err", err) 31 | } 32 | 33 | // create IPC endpoint 34 | ipcpath := ".demo.ipc" 35 | ipclistener, err := net.Listen("unix", ipcpath) 36 | if err != nil { 37 | demo.Log.Crit("IPC endpoint create fail", "err", err) 38 | } 39 | defer os.Remove(ipcpath) 40 | 41 | // mount RPC server on IPC endpoint 42 | // it will automatically detect and serve any valid methods 43 | go func() { 44 | err = rpcsrv.ServeListener(ipclistener) 45 | if err != nil { 46 | demo.Log.Crit("Mount RPC on IPC fail", "err", err) 47 | } 48 | }() 49 | 50 | // create an IPC client 51 | rpcclient, err := rpc.Dial(ipcpath) 52 | if err != nil { 53 | demo.Log.Crit("IPC dial fail", "err", err) 54 | } 55 | 56 | // call the RPC method 57 | var result string 58 | err = rpcclient.Call(&result, "foo_helloWorld") 59 | if err != nil { 60 | demo.Log.Crit("RPC call fail", "err", err) 61 | } 62 | 63 | // inspect the results 64 | demo.Log.Info("RPC return value", "reply", result) 65 | 66 | // bring down the server 67 | rpcsrv.Stop() 68 | } 69 | -------------------------------------------------------------------------------- /p2p/devp2p/B2_Method.go: -------------------------------------------------------------------------------- 1 | // querying the p2p Server through RPC 2 | package main 3 | 4 | import ( 5 | "net" 6 | "os" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/crypto" 10 | "github.com/ethereum/go-ethereum/p2p" 11 | "github.com/ethereum/go-ethereum/rpc" 12 | 13 | demo "./common" 14 | ) 15 | 16 | func main() { 17 | 18 | // make a new private key 19 | privkey, err := crypto.GenerateKey() 20 | if err != nil { 21 | demo.Log.Crit("Generate private key failed", "err", err) 22 | } 23 | 24 | // set up p2p server 25 | cfg := p2p.Config{ 26 | PrivateKey: privkey, 27 | Name: common.MakeName("foo", "42"), 28 | } 29 | srv := p2p.Server{ 30 | Config: cfg, 31 | } 32 | 33 | // attempt to start the p2p server 34 | err = srv.Start() 35 | if err != nil { 36 | demo.Log.Crit("Start p2p.Server failed", "err", err) 37 | } 38 | 39 | // set up the RPC server 40 | rpcsrv := rpc.NewServer() 41 | err = rpcsrv.RegisterName("foo", &srv) 42 | if err != nil { 43 | demo.Log.Crit("Register API method(s) fail", "err", err) 44 | } 45 | 46 | // create IPC endpoint 47 | ipcpath := "demo.ipc" 48 | ipclistener, err := net.Listen("unix", ipcpath) 49 | if err != nil { 50 | demo.Log.Crit("IPC endpoint create fail", "err", err) 51 | } 52 | defer os.Remove(ipcpath) 53 | 54 | // mount RPC server on IPC endpoint 55 | go func() { 56 | err = rpcsrv.ServeListener(ipclistener) 57 | if err != nil { 58 | demo.Log.Crit("Mount RPC on IPC fail", "err", err) 59 | } 60 | }() 61 | 62 | // create a IPC client 63 | rpcclient, err := rpc.Dial(ipcpath) 64 | if err != nil { 65 | demo.Log.Crit("IPC dial fail", "err", err) 66 | } 67 | 68 | // call the RPC method 69 | var nodeinfo p2p.NodeInfo 70 | err = rpcclient.Call(&nodeinfo, "foo_nodeInfo") 71 | if err != nil { 72 | demo.Log.Crit("RPC call fail", "err", err) 73 | } 74 | demo.Log.Info("server started", "enode", nodeinfo.Enode, "name", nodeinfo.Name, "ID", nodeinfo.ID, "IP", nodeinfo.IP) 75 | 76 | // bring down the servers 77 | rpcsrv.Stop() 78 | srv.Stop() 79 | } 80 | -------------------------------------------------------------------------------- /p2p/devp2p/C1_Stack.go: -------------------------------------------------------------------------------- 1 | // set up boilerplate service node and start it 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | 8 | "github.com/ethereum/go-ethereum/node" 9 | 10 | demo "./common" 11 | ) 12 | 13 | const ( 14 | p2pDefaultPort = 30100 15 | ipcpath = ".demo.ipc" 16 | datadirPrefix = ".data_" 17 | ) 18 | 19 | func main() { 20 | // set up the service node 21 | cfg := &node.DefaultConfig 22 | cfg.P2P.ListenAddr = fmt.Sprintf(":%d", p2pDefaultPort) 23 | cfg.IPCPath = ipcpath 24 | cfg.DataDir = fmt.Sprintf("%s%d", datadirPrefix, p2pDefaultPort) 25 | 26 | // create the node instance with the config 27 | stack, err := node.New(cfg) 28 | if err != nil { 29 | demo.Log.Crit("ServiceNode create fail", "err", err) 30 | } 31 | 32 | // start the node 33 | err = stack.Start() 34 | if err != nil { 35 | demo.Log.Crit("ServiceNode start fail", "err", err) 36 | } 37 | defer os.RemoveAll(stack.DataDir()) 38 | 39 | // shut down 40 | err = stack.Stop() 41 | if err != nil { 42 | demo.Log.Crit("Node stop fail", "err", err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /p2p/devp2p/C2_Nodeinfo.go: -------------------------------------------------------------------------------- 1 | // Different ways of accessing RPC API on a servicenode 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/ethereum/go-ethereum/node" 10 | "github.com/ethereum/go-ethereum/p2p" 11 | "github.com/ethereum/go-ethereum/rpc" 12 | 13 | demo "./common" 14 | ) 15 | 16 | var ( 17 | p2pPort = 30100 18 | ipcpath = ".demo.ipc" 19 | datadirPrefix = ".data_" 20 | ) 21 | 22 | func main() { 23 | // set up the service node 24 | cfg := &node.DefaultConfig 25 | cfg.P2P.ListenAddr = fmt.Sprintf(":%d", p2pPort) 26 | cfg.IPCPath = ipcpath 27 | cfg.DataDir = fmt.Sprintf("%s%d", datadirPrefix, p2pPort) 28 | 29 | // create the node instance with the config 30 | stack, err := node.New(cfg) 31 | if err != nil { 32 | demo.Log.Crit("ServiceNode create fail", "err", err) 33 | } 34 | 35 | // start the node 36 | err = stack.Start() 37 | if err != nil { 38 | demo.Log.Crit("ServiceNode start fail", "err", err) 39 | } 40 | defer os.RemoveAll(stack.DataDir()) 41 | 42 | // get the info directly via the p2p server object 43 | p2pserver := stack.Server() 44 | localnodeinfo := p2pserver.NodeInfo() 45 | demo.Log.Info("Nodeinfo from p2p.Server", "enode", localnodeinfo.Enode, "IP", localnodeinfo.IP, "ID", localnodeinfo.ID, "listening address", localnodeinfo.ListenAddr) 46 | 47 | // get the nodeinfo via ServiceNode IPC 48 | localnodeinfo = &p2p.NodeInfo{} 49 | rpcclient, err := stack.Attach() 50 | err = rpcclient.Call(&localnodeinfo, "admin_nodeInfo") 51 | if err != nil { 52 | demo.Log.Crit("Could not get rpcclient via p2p.Server", "err", err) 53 | 54 | } 55 | demo.Log.Info("Nodeinfo from IPC via ServiceNode", "enode", localnodeinfo.Enode, "IP", localnodeinfo.IP, "ID", localnodeinfo.ID, "listening address", localnodeinfo.ListenAddr) 56 | 57 | // get the nodeinfo via external IPC 58 | rpcclient, err = rpc.Dial(filepath.Join(cfg.DataDir, cfg.IPCPath)) 59 | if err != nil { 60 | demo.Log.Crit("Could not get rpcclient via p2p.Server", "err", err) 61 | } 62 | localnodeinfo = &p2p.NodeInfo{} 63 | rpcclient, err = stack.Attach() 64 | err = rpcclient.Call(&localnodeinfo, "admin_nodeInfo") 65 | demo.Log.Info("Nodeinfo from IPC via external call", "enode", localnodeinfo.Enode, "IP", localnodeinfo.IP, "ID", localnodeinfo.ID, "listening address", localnodeinfo.ListenAddr) 66 | 67 | err = stack.Stop() 68 | if err != nil { 69 | demo.Log.Crit("Node stop fail", "err", err) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /p2p/devp2p/C3_Service.go: -------------------------------------------------------------------------------- 1 | // Node stack API using HTTP and WS 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | 8 | "github.com/ethereum/go-ethereum/node" 9 | "github.com/ethereum/go-ethereum/p2p" 10 | "github.com/ethereum/go-ethereum/rpc" 11 | 12 | demo "./common" 13 | ) 14 | 15 | var ( 16 | msgCount = 5 17 | p2pPort = 30100 18 | ipcpath = ".demo.ipc" 19 | datadirPrefix = ".data_" 20 | ) 21 | 22 | // the service we want to offer on the node 23 | // it must implement the node.Service interface 24 | type fooService struct { 25 | V int 26 | } 27 | 28 | // specify API structs that carry the methods we want to use 29 | func (self *fooService) APIs() []rpc.API { 30 | return []rpc.API{ 31 | { 32 | Namespace: "foo", 33 | Version: "0.42", 34 | Service: &FooAPI{self.V}, 35 | Public: true, 36 | }, 37 | { 38 | Namespace: "bar", 39 | Version: "0.666", 40 | Service: &BarAPI{}, 41 | Public: true, 42 | }, 43 | } 44 | } 45 | 46 | // these are needed to satisfy the node.Service interface 47 | // in this example they do nothing 48 | func (self *fooService) Protocols() []p2p.Protocol { 49 | return []p2p.Protocol{} 50 | } 51 | 52 | func (self *fooService) Start(srv *p2p.Server) error { 53 | return nil 54 | } 55 | 56 | func (self *fooService) Stop() error { 57 | return nil 58 | } 59 | 60 | // remember that API structs to be offered MUST be exported 61 | type FooAPI struct { 62 | V int 63 | } 64 | 65 | func (api *FooAPI) GetNumber() (int, error) { 66 | return api.V, nil 67 | } 68 | 69 | type BarAPI struct { 70 | } 71 | 72 | func (api *BarAPI) Double(n int) (int, error) { 73 | return 2 * n, nil 74 | } 75 | 76 | func main() { 77 | 78 | // set up the service node with HTTP and WS 79 | // modules to be available through the different interfaces must be specified explicitly 80 | // Note that IPC exports ALL modules implicitly 81 | cfg := &node.DefaultConfig 82 | cfg.P2P.ListenAddr = fmt.Sprintf(":%d", p2pPort) 83 | cfg.IPCPath = ipcpath 84 | cfg.DataDir = fmt.Sprintf("%s%d", datadirPrefix, p2pPort) 85 | 86 | // HTTP parameters - both module "foo" and "bar" 87 | cfg.HTTPHost = node.DefaultHTTPHost 88 | cfg.HTTPPort = node.DefaultHTTPPort 89 | cfg.HTTPModules = append(cfg.HTTPModules, "foo", "bar") 90 | 91 | // Websocket parameters - only module "foo" 92 | cfg.WSHost = node.DefaultWSHost 93 | cfg.WSPort = node.DefaultWSPort 94 | cfg.WSModules = append(cfg.WSModules, "foo", "baz") 95 | 96 | // create the node instance with the config 97 | stack, err := node.New(cfg) 98 | if err != nil { 99 | demo.Log.Crit("ServiceNode create fail", "err", err) 100 | } 101 | defer os.RemoveAll(stack.DataDir()) 102 | 103 | // wrapper function for servicenode to start the service 104 | foosvc := func(ctx *node.ServiceContext) (node.Service, error) { 105 | return &fooService{42}, nil 106 | } 107 | 108 | // register adds the service to the services the servicenode starts when started 109 | err = stack.Register(foosvc) 110 | if err != nil { 111 | demo.Log.Crit("Register service in ServiceNode failed", "err", err) 112 | } 113 | 114 | // start the node 115 | // after this all features served by the node are available 116 | // thus we can call the API 117 | err = stack.Start() 118 | if err != nil { 119 | demo.Log.Crit("ServiceNode start failed", "err", err) 120 | } 121 | defer os.RemoveAll(cfg.DataDir) 122 | 123 | // the numbers we will pass to the api 124 | var number int 125 | var doublenumber int 126 | 127 | // connect to the RPC 128 | rpcclient_ipc, err := rpc.Dial(fmt.Sprintf("%s/%s", cfg.DataDir, cfg.IPCPath)) 129 | 130 | // Using IPC, get the number from the FooApi 131 | err = rpcclient_ipc.Call(&number, "foo_getNumber") 132 | if err != nil { 133 | demo.Log.Crit("IPC RPC getnumber failed", "err", err) 134 | } 135 | demo.Log.Info("IPC", "getnumber", number) 136 | 137 | // Pass it to BarApi which doubles it 138 | err = rpcclient_ipc.Call(&doublenumber, "bar_double", number) 139 | if err != nil { 140 | demo.Log.Crit("IPC RPC double failed", "err", err) 141 | } 142 | demo.Log.Info("IPC", "double", doublenumber) 143 | 144 | // Same operation with HTTP 145 | // HTTP has both Apis 146 | number = 0 147 | doublenumber = 0 148 | 149 | rpcclient_http, err := rpc.Dial(fmt.Sprintf("http://%s:%d", node.DefaultHTTPHost, node.DefaultHTTPPort)) 150 | 151 | err = rpcclient_http.Call(&number, "foo_getNumber") 152 | if err != nil { 153 | demo.Log.Crit("HTTP RPC getnumber failed", "err", err) 154 | } 155 | demo.Log.Info("HTTP", "getnumber", number) 156 | err = rpcclient_http.Call(&doublenumber, "bar_double", number) 157 | if err != nil { 158 | demo.Log.Crit("HTTP RPC double failed", "err", err) 159 | } 160 | demo.Log.Info("HTTP", "double", doublenumber) 161 | 162 | // Same operation with WS 163 | // we only added the first module to the WS interface, so the second call will fail 164 | number = 0 165 | doublenumber = 0 166 | 167 | rpcclient_ws, err := rpc.Dial(fmt.Sprintf("ws://%s:%d", node.DefaultWSHost, node.DefaultWSPort)) 168 | 169 | err = rpcclient_ws.Call(&number, "foo_getNumber") 170 | if err != nil { 171 | demo.Log.Crit("WS RPC getnumber failed", "err", err) 172 | } 173 | demo.Log.Info("WS", "getnumber", number) 174 | err = rpcclient_ws.Call(&doublenumber, "bar_double", number) 175 | if err == nil { 176 | demo.Log.Crit("WS RPC double should have failed!") 177 | } 178 | demo.Log.Info("WS (double expected fail)", "err", err) 179 | 180 | // bring down the servicenode 181 | err = stack.Stop() 182 | if err != nil { 183 | demo.Log.Crit("Node stop fail", "err", err) 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /p2p/devp2p/D1_Protocols.go: -------------------------------------------------------------------------------- 1 | // Previous "reply" example using p2p.protocols abstraction 2 | package main 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | "sync" 8 | 9 | "github.com/ethereum/go-ethereum/crypto" 10 | "github.com/ethereum/go-ethereum/p2p" 11 | "github.com/ethereum/go-ethereum/p2p/protocols" 12 | 13 | demo "./common" 14 | ) 15 | 16 | var ( 17 | messageW = &sync.WaitGroup{} 18 | ) 19 | 20 | type FooMsg struct { 21 | V uint 22 | } 23 | 24 | // using the protocols abstraction, message structures are registered and their message codes handled automatically 25 | var ( 26 | fooProtocol = protocols.Spec{ 27 | Name: demo.FooProtocolName, 28 | Version: demo.FooProtocolVersion, 29 | MaxMsgSize: demo.FooProtocolMaxMsgSize, 30 | Messages: []interface{}{ 31 | &FooMsg{}, 32 | }, 33 | } 34 | ) 35 | 36 | // the protocols abstraction enables use of an external handler function 37 | type fooHandler struct { 38 | peer *p2p.Peer 39 | } 40 | 41 | func (self *fooHandler) handle(_ context.Context, msg interface{}) error { 42 | foomsg, ok := msg.(*FooMsg) 43 | if !ok { 44 | return fmt.Errorf("invalid message", "msg", msg, "peer", self.peer) 45 | } 46 | demo.Log.Info("received message", "foomsg", foomsg, "peer", self.peer) 47 | return nil 48 | } 49 | 50 | // create the protocol with the protocols extension 51 | var ( 52 | proto = p2p.Protocol{ 53 | Name: "foo", 54 | Version: 42, 55 | Length: 1, 56 | Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 57 | 58 | // create the enhanced peer 59 | pp := protocols.NewPeer(p, rw, &fooProtocol) 60 | 61 | // send the message 62 | outmsg := &FooMsg{ 63 | V: 42, 64 | } 65 | 66 | err := pp.Send(context.TODO(), outmsg) 67 | if err != nil { 68 | demo.Log.Error("Send p2p message fail", "err", err) 69 | } 70 | demo.Log.Info("sending message", "peer", p, "msg", outmsg) 71 | 72 | // protocols abstraction provides a separate blocking run loop for the peer 73 | // a separate handler function is passed to that loop to process incoming messages 74 | run := &fooHandler{ 75 | peer: p, 76 | } 77 | err = pp.Run(run.handle) 78 | 79 | // terminate the protocol 80 | return err 81 | }, 82 | } 83 | ) 84 | 85 | func main() { 86 | 87 | // we need private keys for both servers 88 | privkey_one, err := crypto.GenerateKey() 89 | if err != nil { 90 | demo.Log.Crit("Generate private key #1 failed", "err", err) 91 | } 92 | privkey_two, err := crypto.GenerateKey() 93 | if err != nil { 94 | demo.Log.Crit("Generate private key #2 failed", "err", err) 95 | } 96 | 97 | // set up the two servers 98 | srv_one := demo.NewServer(privkey_one, "foo", "42", proto, 0) 99 | err = srv_one.Start() 100 | if err != nil { 101 | demo.Log.Crit("Start p2p.Server #1 failed", "err", err) 102 | } 103 | 104 | srv_two := demo.NewServer(privkey_two, "bar", "666", proto, 31234) 105 | err = srv_two.Start() 106 | if err != nil { 107 | demo.Log.Crit("Start p2p.Server #2 failed", "err", err) 108 | } 109 | 110 | // set up the event subscriptions on both servers 111 | eventOneC := make(chan *p2p.PeerEvent) 112 | sub_one := srv_one.SubscribeEvents(eventOneC) 113 | messageW.Add(1) 114 | go func() { 115 | for { 116 | select { 117 | case peerevent := <-eventOneC: 118 | if peerevent.Type == "add" { 119 | demo.Log.Debug("Received peer add notification on node #1", "peer", peerevent.Peer) 120 | } else if peerevent.Type == "msgrecv" { 121 | demo.Log.Info("Received message nofification on node #1", "event", peerevent) 122 | messageW.Done() 123 | } 124 | case <-sub_one.Err(): 125 | return 126 | } 127 | } 128 | }() 129 | 130 | eventTwoC := make(chan *p2p.PeerEvent) 131 | sub_two := srv_two.SubscribeEvents(eventTwoC) 132 | messageW.Add(1) 133 | go func() { 134 | for { 135 | select { 136 | case peerevent := <-eventTwoC: 137 | if peerevent.Type == "add" { 138 | demo.Log.Debug("Received peer add notification on node #2", "peer", peerevent.Peer) 139 | } else if peerevent.Type == "msgrecv" { 140 | demo.Log.Info("Received message nofification on node #2", "event", peerevent) 141 | messageW.Done() 142 | } 143 | case <-sub_two.Err(): 144 | return 145 | } 146 | } 147 | }() 148 | 149 | // get the node instance of the second server 150 | node_two := srv_two.Self() 151 | 152 | // add it as a peer to the first node 153 | // the connection and crypto handshake will be performed automatically 154 | srv_one.AddPeer(node_two) 155 | 156 | // wait for each respective message to be delivered on both sides 157 | messageW.Wait() 158 | 159 | // terminate subscription loops and unsubscribe 160 | sub_one.Unsubscribe() 161 | sub_two.Unsubscribe() 162 | 163 | // stop the servers 164 | srv_one.Stop() 165 | srv_two.Stop() 166 | } 167 | -------------------------------------------------------------------------------- /p2p/devp2p/D2_Multiservice.go: -------------------------------------------------------------------------------- 1 | // multiple services in same node 2 | package main 3 | 4 | import ( 5 | // "fmt" 6 | "os" 7 | 8 | "github.com/ethereum/go-ethereum/node" 9 | "github.com/ethereum/go-ethereum/p2p" 10 | "github.com/ethereum/go-ethereum/rpc" 11 | 12 | demo "./common" 13 | ) 14 | 15 | // the fooservice retrieves the shared value 16 | type fooService struct { 17 | v *int 18 | } 19 | 20 | func newFooService(v *int) *fooService { 21 | return &fooService{ 22 | v: v, 23 | } 24 | } 25 | 26 | func (self *fooService) APIs() []rpc.API { 27 | return []rpc.API{ 28 | { 29 | Namespace: "foo", 30 | Version: "0.42", 31 | Service: &FooAPI{self.v}, 32 | Public: true, 33 | }, 34 | } 35 | } 36 | 37 | func (self *fooService) Protocols() []p2p.Protocol { 38 | return []p2p.Protocol{} 39 | } 40 | 41 | func (self *fooService) Start(srv *p2p.Server) error { 42 | return nil 43 | } 44 | 45 | func (self *fooService) Stop() error { 46 | return nil 47 | } 48 | 49 | type FooAPI struct { 50 | v *int 51 | } 52 | 53 | func (api *FooAPI) Get() (int, error) { 54 | return *api.v, nil 55 | } 56 | 57 | // the barservice sets the shared value 58 | type barService struct { 59 | v *int 60 | } 61 | 62 | func newBarService(v *int) *barService { 63 | return &barService{ 64 | v: v, 65 | } 66 | } 67 | 68 | func (self *barService) APIs() []rpc.API { 69 | return []rpc.API{ 70 | { 71 | Namespace: "bar", 72 | Version: "0.42", 73 | Service: &BarAPI{self.v}, 74 | Public: true, 75 | }, 76 | } 77 | } 78 | 79 | func (self *barService) Protocols() []p2p.Protocol { 80 | return []p2p.Protocol{} 81 | } 82 | 83 | func (self *barService) Start(srv *p2p.Server) error { 84 | return nil 85 | } 86 | 87 | func (self *barService) Stop() error { 88 | return nil 89 | } 90 | 91 | type BarAPI struct { 92 | v *int 93 | } 94 | 95 | func (api *BarAPI) Set(n int) error { 96 | *api.v = n 97 | return nil 98 | } 99 | 100 | func main() { 101 | 102 | var sharedvalue int 103 | 104 | stack, err := demo.NewServiceNode(demo.P2pPort, 0, 0) 105 | 106 | // register two separate services 107 | foosvc := func(ctx *node.ServiceContext) (node.Service, error) { 108 | return newFooService(&sharedvalue), nil 109 | } 110 | err = stack.Register(foosvc) 111 | if err != nil { 112 | demo.Log.Crit("Register fooservice in servicenode failed", "err", err) 113 | } 114 | 115 | barsvc := func(ctx *node.ServiceContext) (node.Service, error) { 116 | return newBarService(&sharedvalue), nil 117 | } 118 | err = stack.Register(barsvc) 119 | if err != nil { 120 | demo.Log.Crit("Register barservice in servicenode failed", "err", err) 121 | } 122 | defer os.RemoveAll(stack.DataDir()) 123 | 124 | // start the node 125 | err = stack.Start() 126 | if err != nil { 127 | demo.Log.Crit("servicenode start failed", "err", err) 128 | } 129 | 130 | // set the shared value in service bar 131 | rpcclient, err := stack.Attach() 132 | err = rpcclient.Call(nil, "bar_set", 42) 133 | if err != nil { 134 | demo.Log.Crit("Could not get rpcclient via p2p.Server", "err", err) 135 | 136 | } 137 | 138 | // get the shared value in service foo 139 | var result int 140 | err = rpcclient.Call(&result, "foo_get") 141 | if err != nil { 142 | demo.Log.Crit("Could not get rpcclient via p2p.Server", "err", err) 143 | 144 | } 145 | demo.Log.Info("get", "result", result, "sharedvalue", sharedvalue) 146 | 147 | // bring down the servicenode 148 | stack.Stop() 149 | } 150 | -------------------------------------------------------------------------------- /p2p/devp2p/E1_Pss.go: -------------------------------------------------------------------------------- 1 | // pss send-to-self hello world 2 | package main 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | "github.com/ethereum/go-ethereum/common" 11 | "github.com/ethereum/go-ethereum/crypto" 12 | "github.com/ethereum/go-ethereum/node" 13 | "github.com/ethersphere/swarm" 14 | bzzapi "github.com/ethersphere/swarm/api" 15 | "github.com/ethersphere/swarm/pss" 16 | 17 | demo "./common" 18 | ) 19 | 20 | func newService(bzzdir string, bzzport int, bzznetworkid uint64) func(ctx *node.ServiceContext) (node.Service, error) { 21 | return func(ctx *node.ServiceContext) (node.Service, error) { 22 | 23 | // generate a new private key 24 | privkey, err := crypto.GenerateKey() 25 | if err != nil { 26 | demo.Log.Crit("private key generate servicenode 'left' fail: %v") 27 | } 28 | 29 | // create necessary swarm params 30 | bzzconfig := bzzapi.NewConfig() 31 | bzzconfig.Path = bzzdir 32 | bzzconfig.Init(privkey, privkey) 33 | if err != nil { 34 | demo.Log.Crit("unable to configure swarm", "err", err) 35 | } 36 | bzzconfig.Port = fmt.Sprintf("%d", bzzport) 37 | 38 | // shortcut to setting up a swarm node 39 | return swarm.NewSwarm(bzzconfig, nil) 40 | } 41 | } 42 | 43 | func main() { 44 | 45 | // create two nodes 46 | l_stack, err := demo.NewServiceNode(demo.P2pPort, 0, 0) 47 | if err != nil { 48 | demo.Log.Crit(err.Error()) 49 | } 50 | r_stack, err := demo.NewServiceNode(demo.P2pPort+1, 0, 0) 51 | if err != nil { 52 | demo.Log.Crit(err.Error()) 53 | } 54 | 55 | // register the pss activated bzz services 56 | l_svc := newService(l_stack.InstanceDir(), demo.BzzDefaultPort, demo.BzzDefaultNetworkId) 57 | err = l_stack.Register(l_svc) 58 | if err != nil { 59 | demo.Log.Crit("servicenode 'left' pss register fail", "err", err) 60 | } 61 | r_svc := newService(r_stack.InstanceDir(), demo.BzzDefaultPort+1, demo.BzzDefaultNetworkId) 62 | err = r_stack.Register(r_svc) 63 | if err != nil { 64 | demo.Log.Crit("servicenode 'right' pss register fail", "err", err) 65 | } 66 | 67 | // start the nodes 68 | err = l_stack.Start() 69 | if err != nil { 70 | demo.Log.Crit("servicenode start failed", "err", err) 71 | } 72 | defer os.RemoveAll(l_stack.DataDir()) 73 | err = r_stack.Start() 74 | if err != nil { 75 | demo.Log.Crit("servicenode start failed", "err", err) 76 | } 77 | defer os.RemoveAll(r_stack.DataDir()) 78 | 79 | // connect the nodes to the middle 80 | l_stack.Server().AddPeer(r_stack.Server().Self()) 81 | 82 | // get the rpc clients 83 | l_rpcclient, err := l_stack.Attach() 84 | r_rpcclient, err := r_stack.Attach() 85 | 86 | // wait until the state of the swarm overlay network is ready 87 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 88 | defer cancel() 89 | err = demo.WaitHealthy(ctx, 2, l_rpcclient, r_rpcclient) 90 | if err != nil { 91 | demo.Log.Crit("health check fail", "err", err) 92 | } 93 | // ... but the healthy functions doesnt seem to work, so we're stuck with timeout for now 94 | time.Sleep(time.Second) 95 | 96 | // get a valid topic byte 97 | var topic string 98 | err = l_rpcclient.Call(&topic, "pss_stringToTopic", "foo") 99 | if err != nil { 100 | demo.Log.Crit("pss string to topic fail", "err", err) 101 | } 102 | 103 | // subscribe to incoming messages on the receiving sevicenode 104 | // this will register a message handler on the specified topic 105 | msgC := make(chan pss.APIMsg) 106 | sub, err := r_rpcclient.Subscribe(context.Background(), "pss", msgC, "receive", topic, false, false) 107 | 108 | // get the recipient node's swarm overlay address 109 | var r_bzzaddr string 110 | err = r_rpcclient.Call(&r_bzzaddr, "pss_baseAddr") 111 | if err != nil { 112 | demo.Log.Crit("pss get pubkey fail", "err", err) 113 | } 114 | 115 | // get the receiver's public key 116 | var r_pubkey string 117 | err = r_rpcclient.Call(&r_pubkey, "pss_getPublicKey") 118 | if err != nil { 119 | demo.Log.Crit("pss get pubkey fail", "err", err) 120 | } 121 | 122 | // make the sender aware of the receiver's public key 123 | err = l_rpcclient.Call(nil, "pss_setPeerPublicKey", r_pubkey, topic, r_bzzaddr) 124 | if err != nil { 125 | demo.Log.Crit("pss get pubkey fail", "err", err) 126 | } 127 | 128 | // send message using asymmetric encryption 129 | // since it's sent to ourselves, it will not go through pss forwarding 130 | err = l_rpcclient.Call(nil, "pss_sendAsym", r_pubkey, topic, common.ToHex([]byte("bar"))) 131 | if err != nil { 132 | demo.Log.Crit("pss send fail", "err", err) 133 | } 134 | 135 | // get the incoming message 136 | inmsg := <-msgC 137 | demo.Log.Info("pss received", "msg", string(inmsg.Msg), "from", fmt.Sprintf("%x", inmsg.Key)) 138 | 139 | // bring down the servicenodes 140 | sub.Unsubscribe() 141 | r_rpcclient.Close() 142 | l_rpcclient.Close() 143 | r_stack.Stop() 144 | l_stack.Stop() 145 | } 146 | -------------------------------------------------------------------------------- /p2p/devp2p/E2_PssRouting.go: -------------------------------------------------------------------------------- 1 | // pss send-to-self hello world 2 | package main 3 | 4 | import ( 5 | "context" 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | "github.com/ethereum/go-ethereum/common" 11 | "github.com/ethereum/go-ethereum/crypto" 12 | "github.com/ethereum/go-ethereum/node" 13 | "github.com/ethereum/go-ethereum/swarm" 14 | bzzapi "github.com/ethereum/go-ethereum/swarm/api" 15 | "github.com/ethereum/go-ethereum/swarm/pss" 16 | 17 | demo "./common" 18 | ) 19 | 20 | func newService(bzzdir string, bzzport int, bzznetworkid uint64) func(ctx *node.ServiceContext) (node.Service, error) { 21 | return func(ctx *node.ServiceContext) (node.Service, error) { 22 | 23 | // generate a new private key 24 | privkey, err := crypto.GenerateKey() 25 | if err != nil { 26 | demo.Log.Crit("private key generate servicenode 'left' fail: %v") 27 | } 28 | 29 | // create necessary swarm params 30 | bzzconfig := bzzapi.NewConfig() 31 | bzzconfig.Path = bzzdir 32 | bzzconfig.Init(privkey) 33 | if err != nil { 34 | demo.Log.Crit("unable to configure swarm", "err", err) 35 | } 36 | bzzconfig.Port = fmt.Sprintf("%d", bzzport) 37 | 38 | // shortcut to setting up a swarm node 39 | return swarm.NewSwarm(bzzconfig, nil) 40 | 41 | } 42 | } 43 | 44 | func main() { 45 | 46 | // create three nodes 47 | l_stack, err := demo.NewServiceNode(demo.P2pPort, 0, 0) 48 | if err != nil { 49 | demo.Log.Crit(err.Error()) 50 | } 51 | r_stack, err := demo.NewServiceNode(demo.P2pPort+1, 0, 0) 52 | if err != nil { 53 | demo.Log.Crit(err.Error()) 54 | } 55 | c_stack, err := demo.NewServiceNode(demo.P2pPort+2, 0, 0) 56 | if err != nil { 57 | demo.Log.Crit(err.Error()) 58 | } 59 | 60 | // register the pss activated bzz services 61 | l_svc := newService(l_stack.InstanceDir(), demo.BzzDefaultPort, demo.BzzDefaultNetworkId) 62 | err = l_stack.Register(l_svc) 63 | if err != nil { 64 | demo.Log.Crit("servicenode 'left' pss register fail", "err", err) 65 | } 66 | r_svc := newService(r_stack.InstanceDir(), demo.BzzDefaultPort+1, demo.BzzDefaultNetworkId) 67 | err = r_stack.Register(r_svc) 68 | if err != nil { 69 | demo.Log.Crit("servicenode 'right' pss register fail", "err", err) 70 | } 71 | c_svc := newService(c_stack.InstanceDir(), demo.BzzDefaultPort+2, demo.BzzDefaultNetworkId) 72 | err = c_stack.Register(c_svc) 73 | if err != nil { 74 | demo.Log.Crit("servicenode 'right' pss register fail", "err", err) 75 | } 76 | 77 | // start the nodes 78 | err = l_stack.Start() 79 | if err != nil { 80 | demo.Log.Crit("servicenode start failed", "err", err) 81 | } 82 | defer os.RemoveAll(l_stack.DataDir()) 83 | err = r_stack.Start() 84 | if err != nil { 85 | demo.Log.Crit("servicenode start failed", "err", err) 86 | } 87 | defer os.RemoveAll(r_stack.DataDir()) 88 | err = c_stack.Start() 89 | if err != nil { 90 | demo.Log.Crit("servicenode start failed", "err", err) 91 | } 92 | defer os.RemoveAll(c_stack.DataDir()) 93 | 94 | // connect the nodes to the middle 95 | c_stack.Server().AddPeer(l_stack.Server().Self()) 96 | c_stack.Server().AddPeer(r_stack.Server().Self()) 97 | 98 | // get the rpc clients 99 | l_rpcclient, err := l_stack.Attach() 100 | r_rpcclient, err := r_stack.Attach() 101 | 102 | // wait until the state of the swarm overlay network is ready 103 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 104 | defer cancel() 105 | err = demo.WaitHealthy(ctx, 2, l_rpcclient, r_rpcclient) 106 | if err != nil { 107 | demo.Log.Crit("health check fail", "err", err) 108 | } 109 | time.Sleep(time.Second) // because the healthy does not work 110 | 111 | // get a valid topic byte 112 | var topic string 113 | err = l_rpcclient.Call(&topic, "pss_stringToTopic", "foo") 114 | if err != nil { 115 | demo.Log.Crit("pss string to topic fail", "err", err) 116 | } 117 | 118 | // subscribe to incoming messages on the receiving sevicenode 119 | // this will register a message handler on the specified topic 120 | msgC := make(chan pss.APIMsg) 121 | sub, err := r_rpcclient.Subscribe(context.Background(), "pss", msgC, "receive", topic, false, false) 122 | 123 | // supply no address for routing 124 | r_bzzaddr := "0x" 125 | 126 | // get the receiver's public key 127 | var r_pubkey string 128 | err = r_rpcclient.Call(&r_pubkey, "pss_getPublicKey") 129 | if err != nil { 130 | demo.Log.Crit("pss get pubkey fail", "err", err) 131 | } 132 | 133 | // make the sender aware of the receiver's public key 134 | err = l_rpcclient.Call(nil, "pss_setPeerPublicKey", r_pubkey, topic, r_bzzaddr) 135 | if err != nil { 136 | demo.Log.Crit("pss get pubkey fail", "err", err) 137 | } 138 | 139 | // send message using asymmetric encryption 140 | // since it's sent to ourselves, it will not go through pss forwarding 141 | err = l_rpcclient.Call(nil, "pss_sendAsym", r_pubkey, topic, common.ToHex([]byte("bar"))) 142 | if err != nil { 143 | demo.Log.Crit("pss send fail", "err", err) 144 | } 145 | 146 | // get the incoming message 147 | inmsg := <-msgC 148 | demo.Log.Info("pss received", "msg", string(inmsg.Msg), "from", fmt.Sprintf("%x", inmsg.Key)) 149 | 150 | // bring down the servicenodes 151 | sub.Unsubscribe() 152 | r_rpcclient.Close() 153 | l_rpcclient.Close() 154 | c_stack.Stop() 155 | r_stack.Stop() 156 | l_stack.Stop() 157 | } 158 | -------------------------------------------------------------------------------- /p2p/devp2p/E3_PssSym.go: -------------------------------------------------------------------------------- 1 | // pss send symmetrically encrypted message 2 | package main 3 | 4 | import ( 5 | "context" 6 | "crypto/rand" 7 | "fmt" 8 | "os" 9 | "time" 10 | 11 | "github.com/ethereum/go-ethereum/common" 12 | "github.com/ethereum/go-ethereum/crypto" 13 | "github.com/ethereum/go-ethereum/node" 14 | "github.com/ethereum/go-ethereum/swarm" 15 | bzzapi "github.com/ethereum/go-ethereum/swarm/api" 16 | "github.com/ethereum/go-ethereum/swarm/pss" 17 | 18 | demo "./common" 19 | ) 20 | 21 | func newService(bzzdir string, bzzport int, bzznetworkid uint64) func(ctx *node.ServiceContext) (node.Service, error) { 22 | return func(ctx *node.ServiceContext) (node.Service, error) { 23 | 24 | // generate a new private key 25 | privkey, err := crypto.GenerateKey() 26 | if err != nil { 27 | demo.Log.Crit("private key generate servicenode 'left' fail: %v") 28 | } 29 | 30 | // create necessary swarm params 31 | bzzconfig := bzzapi.NewConfig() 32 | bzzconfig.Path = bzzdir 33 | bzzconfig.Init(privkey) 34 | if err != nil { 35 | demo.Log.Crit("unable to configure swarm", "err", err) 36 | } 37 | bzzconfig.Port = fmt.Sprintf("%d", bzzport) 38 | 39 | // shortcut to setting up a swarm node 40 | return swarm.NewSwarm(bzzconfig, nil) 41 | 42 | } 43 | } 44 | 45 | func main() { 46 | 47 | // create two nodes 48 | l_stack, err := demo.NewServiceNode(demo.P2pPort, 0, 0) 49 | if err != nil { 50 | demo.Log.Crit(err.Error()) 51 | } 52 | r_stack, err := demo.NewServiceNode(demo.P2pPort+1, 0, 0) 53 | if err != nil { 54 | demo.Log.Crit(err.Error()) 55 | } 56 | 57 | // register the pss activated bzz services 58 | l_svc := newService(l_stack.InstanceDir(), demo.BzzDefaultPort, demo.BzzDefaultNetworkId) 59 | err = l_stack.Register(l_svc) 60 | if err != nil { 61 | demo.Log.Crit("servicenode 'left' pss register fail", "err", err) 62 | } 63 | r_svc := newService(r_stack.InstanceDir(), demo.BzzDefaultPort+1, demo.BzzDefaultNetworkId) 64 | err = r_stack.Register(r_svc) 65 | if err != nil { 66 | demo.Log.Crit("servicenode 'right' pss register fail", "err", err) 67 | } 68 | 69 | // start the nodes 70 | err = l_stack.Start() 71 | if err != nil { 72 | demo.Log.Crit("servicenode start failed", "err", err) 73 | } 74 | defer os.RemoveAll(l_stack.DataDir()) 75 | err = r_stack.Start() 76 | if err != nil { 77 | demo.Log.Crit("servicenode start failed", "err", err) 78 | } 79 | defer os.RemoveAll(r_stack.DataDir()) 80 | 81 | // connect the nodes to the middle 82 | l_stack.Server().AddPeer(r_stack.Server().Self()) 83 | 84 | // get the rpc clients 85 | l_rpcclient, err := l_stack.Attach() 86 | r_rpcclient, err := r_stack.Attach() 87 | 88 | // wait until the state of the swarm overlay network is ready 89 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 90 | defer cancel() 91 | err = demo.WaitHealthy(ctx, 2, l_rpcclient, r_rpcclient) 92 | if err != nil { 93 | demo.Log.Crit("health check fail", "err", err) 94 | } 95 | time.Sleep(time.Second) // because the healthy does not work 96 | 97 | // get a valid topic byte 98 | var topic string 99 | err = l_rpcclient.Call(&topic, "pss_stringToTopic", "foo") 100 | if err != nil { 101 | demo.Log.Crit("pss string to topic fail", "err", err) 102 | } 103 | 104 | // subscribe to incoming messages on the receiving sevicenode 105 | // this will register a message handler on the specified topic 106 | msgC := make(chan pss.APIMsg) 107 | sub, err := r_rpcclient.Subscribe(context.Background(), "pss", msgC, "receive", topic, false, false) 108 | 109 | // get the recipient node's swarm overlay address 110 | var l_bzzaddr string 111 | err = r_rpcclient.Call(&l_bzzaddr, "pss_baseAddr") 112 | if err != nil { 113 | demo.Log.Crit("pss get baseaddr fail", "err", err) 114 | } 115 | var r_bzzaddr string 116 | err = r_rpcclient.Call(&r_bzzaddr, "pss_baseAddr") 117 | if err != nil { 118 | demo.Log.Crit("pss get baseaddr fail", "err", err) 119 | } 120 | 121 | symkey := make([]byte, 32) 122 | c, err := rand.Read(symkey) 123 | if err != nil { 124 | demo.Log.Crit("symkey gen fail", "err", err) 125 | } else if c < 32 { 126 | demo.Log.Crit("symkey size mismatch, expected 32", "size", c) 127 | } 128 | 129 | var l_symkeyid string 130 | err = l_rpcclient.Call(&l_symkeyid, "pss_setSymmetricKey", symkey, topic, r_bzzaddr, true) 131 | if err != nil { 132 | demo.Log.Crit("pss set symkey fail", "err", err) 133 | } 134 | 135 | var r_symkeyid string 136 | err = r_rpcclient.Call(&r_symkeyid, "pss_setSymmetricKey", symkey, topic, l_bzzaddr, true) 137 | if err != nil { 138 | demo.Log.Crit("pss set symkey fail", "err", err) 139 | } 140 | 141 | // send message using symmetric encryption 142 | // since it's sent to ourselves, it will not go through pss forwarding 143 | err = l_rpcclient.Call(nil, "pss_sendSym", l_symkeyid, topic, common.ToHex([]byte("bar"))) 144 | if err != nil { 145 | demo.Log.Crit("pss send fail", "err", err) 146 | } 147 | 148 | // get the incoming message 149 | inmsg := <-msgC 150 | demo.Log.Info("pss received", "msg", string(inmsg.Msg), "from", fmt.Sprintf("%x", inmsg.Key)) 151 | 152 | // bring down the servicenodes 153 | sub.Unsubscribe() 154 | r_rpcclient.Close() 155 | l_rpcclient.Close() 156 | r_stack.Stop() 157 | l_stack.Stop() 158 | } 159 | -------------------------------------------------------------------------------- /p2p/devp2p/E4_PssRaw.go: -------------------------------------------------------------------------------- 1 | // pss send message using external encryption 2 | package main 3 | 4 | import ( 5 | "context" 6 | "crypto/rand" 7 | "fmt" 8 | "os" 9 | "time" 10 | 11 | "github.com/ethereum/go-ethereum/common" 12 | "github.com/ethereum/go-ethereum/crypto" 13 | "github.com/ethereum/go-ethereum/crypto/ecies" 14 | "github.com/ethereum/go-ethereum/node" 15 | "github.com/ethereum/go-ethereum/swarm" 16 | bzzapi "github.com/ethereum/go-ethereum/swarm/api" 17 | "github.com/ethereum/go-ethereum/swarm/pss" 18 | 19 | demo "./common" 20 | ) 21 | 22 | func newService(bzzdir string, bzzport int, bzznetworkid uint64) func(ctx *node.ServiceContext) (node.Service, error) { 23 | return func(ctx *node.ServiceContext) (node.Service, error) { 24 | 25 | // generate a new private key 26 | privkey, err := crypto.GenerateKey() 27 | if err != nil { 28 | demo.Log.Crit("private key generate servicenode 'left' fail: %v") 29 | } 30 | 31 | // create necessary swarm params 32 | bzzconfig := bzzapi.NewConfig() 33 | bzzconfig.Path = bzzdir 34 | bzzconfig.Pss.AllowRaw = true 35 | bzzconfig.Init(privkey) 36 | if err != nil { 37 | demo.Log.Crit("unable to configure swarm", "err", err) 38 | } 39 | bzzconfig.Port = fmt.Sprintf("%d", bzzport) 40 | 41 | // shortcut to setting up a swarm node 42 | return swarm.NewSwarm(bzzconfig, nil) 43 | 44 | } 45 | } 46 | 47 | func main() { 48 | 49 | // create two nodes 50 | l_stack, err := demo.NewServiceNode(demo.P2pPort, 0, 0) 51 | if err != nil { 52 | demo.Log.Crit(err.Error()) 53 | } 54 | r_stack, err := demo.NewServiceNode(demo.P2pPort+1, 0, 0) 55 | if err != nil { 56 | demo.Log.Crit(err.Error()) 57 | } 58 | 59 | // register the pss activated bzz services 60 | l_svc := newService(l_stack.InstanceDir(), demo.BzzDefaultPort, demo.BzzDefaultNetworkId) 61 | err = l_stack.Register(l_svc) 62 | if err != nil { 63 | demo.Log.Crit("servicenode 'left' pss register fail", "err", err) 64 | } 65 | r_svc := newService(r_stack.InstanceDir(), demo.BzzDefaultPort+1, demo.BzzDefaultNetworkId) 66 | err = r_stack.Register(r_svc) 67 | if err != nil { 68 | demo.Log.Crit("servicenode 'right' pss register fail", "err", err) 69 | } 70 | 71 | // start the nodes 72 | err = l_stack.Start() 73 | if err != nil { 74 | demo.Log.Crit("servicenode start failed", "err", err) 75 | } 76 | defer os.RemoveAll(l_stack.DataDir()) 77 | err = r_stack.Start() 78 | if err != nil { 79 | demo.Log.Crit("servicenode start failed", "err", err) 80 | } 81 | defer os.RemoveAll(r_stack.DataDir()) 82 | 83 | // connect the nodes to the middle 84 | l_stack.Server().AddPeer(r_stack.Server().Self()) 85 | 86 | // get the rpc clients 87 | l_rpcclient, err := l_stack.Attach() 88 | r_rpcclient, err := r_stack.Attach() 89 | 90 | // wait until the state of the swarm overlay network is ready 91 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 92 | defer cancel() 93 | err = demo.WaitHealthy(ctx, 2, l_rpcclient, r_rpcclient) 94 | if err != nil { 95 | demo.Log.Crit("health check fail", "err", err) 96 | } 97 | time.Sleep(time.Second) // because the healthy does not work 98 | 99 | // get a valid topic byte 100 | var topic string 101 | err = l_rpcclient.Call(&topic, "pss_stringToTopic", "foo") 102 | if err != nil { 103 | demo.Log.Crit("pss string to topic fail", "err", err) 104 | } 105 | 106 | // subscribe to incoming messages on the receiving sevicenode 107 | // this will register a message handler on the specified topic 108 | msgC := make(chan pss.APIMsg) 109 | sub, err := r_rpcclient.Subscribe(context.Background(), "pss", msgC, "receive", topic, true, false) 110 | 111 | // get the recipient node's swarm overlay address 112 | var l_bzzaddr string 113 | err = r_rpcclient.Call(&l_bzzaddr, "pss_baseAddr") 114 | if err != nil { 115 | demo.Log.Crit("pss get baseaddr fail", "err", err) 116 | } 117 | var r_bzzaddr string 118 | err = r_rpcclient.Call(&r_bzzaddr, "pss_baseAddr") 119 | if err != nil { 120 | demo.Log.Crit("pss get baseaddr fail", "err", err) 121 | } 122 | 123 | // generate the encryption key to use and encrypt the message with it 124 | r_externalkey, err := ecies.GenerateKey(rand.Reader, crypto.S256(), nil) 125 | if err != nil { 126 | demo.Log.Crit("generate external encryption key fail", "err", err) 127 | } 128 | m := []byte("xyzzy") 129 | ciphertext, err := ecies.Encrypt(rand.Reader, &r_externalkey.PublicKey, m, nil, nil) 130 | if err != nil { 131 | demo.Log.Crit("external message encryption fail", "err", err) 132 | } 133 | 134 | // send message using symmetric encryption 135 | // since it's sent to ourselves, it will not go through pss forwarding 136 | err = l_rpcclient.Call(nil, "pss_sendRaw", r_bzzaddr, topic, common.ToHex(ciphertext)) 137 | if err != nil { 138 | demo.Log.Crit("pss send fail", "err", err) 139 | } 140 | 141 | // get the incoming message 142 | inmsg := <-msgC 143 | 144 | // decrypt the message 145 | plaintext, err := r_externalkey.Decrypt(inmsg.Msg, nil, nil) 146 | demo.Log.Info("pss received", "msg", string(plaintext), "from", fmt.Sprintf("%x", inmsg.Key)) 147 | 148 | // bring down the servicenodes 149 | sub.Unsubscribe() 150 | r_rpcclient.Close() 151 | l_rpcclient.Close() 152 | r_stack.Stop() 153 | l_stack.Stop() 154 | } 155 | -------------------------------------------------------------------------------- /p2p/devp2p/F1_PssGoInit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/ethereum/go-ethereum/crypto" 7 | "github.com/ethereum/go-ethereum/p2p/enode" 8 | "github.com/ethereum/go-ethereum/swarm/network" 9 | "github.com/ethereum/go-ethereum/swarm/pss" 10 | 11 | demo "./common" 12 | ) 13 | 14 | func main() { 15 | 16 | // generate a new private key 17 | privkey, err := crypto.GenerateKey() 18 | if err != nil { 19 | demo.Log.Crit("private key generate servicenode 'left' fail", "err", err) 20 | } 21 | 22 | // constructor configuration for the bzz service bundle 23 | bzzaddr := network.PrivateKeyToBzzKey(privkey) 24 | hiveconfig := network.NewHiveParams() 25 | enod := enode.NewV4(&privkey.PublicKey, net.IPv4(127, 0, 0, 1), 0, 0) // tmp 26 | bzzconfig := &network.BzzConfig{ 27 | OverlayAddr: bzzaddr, 28 | UnderlayAddr: []byte(enod.String()), 29 | NetworkID: 666, 30 | HiveParams: hiveconfig, 31 | } 32 | 33 | // kademlia object controls the node connection tables 34 | kadparams := network.NewKadParams() 35 | kad := network.NewKademlia( 36 | bzzaddr, 37 | kadparams, 38 | ) 39 | 40 | // bzz provides connectivity between swarm nodes (handshake) 41 | bz := network.NewBzz(bzzconfig, kad, nil, nil, nil) 42 | 43 | // set up pss with the same kademlia as the swarm instance 44 | pp := pss.NewPssParams().WithPrivateKey(privkey) 45 | ps, err := pss.NewPss(kad, pp) 46 | if err != nil { 47 | demo.Log.Crit("PSS create fail", "err", err) 48 | } 49 | 50 | demo.Log.Info("done", "pss", ps, "bzz", bz) 51 | } 52 | -------------------------------------------------------------------------------- /p2p/devp2p/README.md: -------------------------------------------------------------------------------- 1 | # p2p programming in go-ethereum 2 | 3 | These code examples are intended to demonstrate the main building blocks of peer-to-peer comunications in go-ethereum. They are organized in chapters, where every example in a chapter builds on the next. 4 | 5 | They form the basis for a tutorial with a detailed simple-to-follow narrative. This tutorial is currently being written, and includes to date explanations of examples **A1** through **A4**. 6 | 7 | Please note that the texts are still in draft status, and may contain omissions, typos, shitty language and most certainly bad formatting. They is written in `latex`, but for convenience a README.md file will be generated with each update of the texts. It will be auto-generated by `pandoc` and may contain unparsed directives. It will stay that way until the document is complete, where proper builds will be made available. 8 | 9 | ## USING THESE EXAMPLES 10 | 11 | ``` 12 | go run [-v] 13 | ``` 14 | 15 | ## TODO 16 | 17 | * Write general introduction to components in go-ethereum devp2p 18 | * Write accompanying tutorials 19 | * Add example of lowlevel PSS implementation 20 | 21 | ## Contents 22 | 23 | ### A - Lowlevel devp2p 24 | 25 | p2p server is the core structure for communications between nodes. It creates and maintains ip connections between nodes, and embeds p2p encryption with RLP serialization (RLPx) on the data connection. 26 | 27 | * A1_Server.go 28 | 29 | Initializing and starting the p2p Server 30 | 31 | * A2_Connect.go 32 | 33 | Connecting two p2p Servers 34 | 35 | * A3_Events.go 36 | 37 | Receiving notification of completed connection 38 | 39 | * A4_Message.go 40 | 41 | Sending a message from server A to server B 42 | 43 | * A5_Reply.go 44 | 45 | A sample p2p ping protocol implementation 46 | 47 | ### B - Remote Procedure Calls 48 | 49 | * B1_RPC.go 50 | 51 | Create IPC RPC server with one method and call it 52 | 53 | * B2_Method.go 54 | 55 | Retrieve information from p2p server through RPC 56 | 57 | * B3_Message.go 58 | 59 | Receive notification of p2p messaging events through RPC 60 | 61 | ### C - Service node 62 | 63 | This entity encapsulates the p2p server and the RPC call APIs, in packages called "services." A Node.Service is defined as an interface, and objects of this interface are registered with the node, and automatically started in alphabetical order when the service node is started. 64 | 65 | * C1_Stack.go 66 | 67 | Create and start a service node 68 | 69 | * C2_Nodeinfo.go 70 | 71 | Local accessors to RPC APIs 72 | 73 | * C3_Service.go 74 | 75 | Defining and running a service 76 | 77 | * C4_Full.go 78 | 79 | Servicenode ping protocol implementation controlled through RPC 80 | 81 | ### D - Complex nodes 82 | 83 | `devp2p` provides a framework for designing autonomous protocol handling code. This chapter shows how to implement one, and how to combine several services providing their own APIs and protocols in the same service node. 84 | 85 | * D1_Protocols.go 86 | 87 | p2p protocol abstraction layer enabling automatic recognition of messages and external message handlers 88 | 89 | * D2_Multiservice.go 90 | 91 | Registering multiple services with the service node 92 | 93 | ### E - Pss 94 | 95 | Pss enables encrypted messaging between nodes that aren't directly connected through p2p server, by relaying the message through nodes between them. Relaying is done with swarm's kademlia routing. The message is encrypted end-to-end using ephemeral public key cryptography. 96 | 97 | * E1_Pss.go 98 | 99 | Set up a pss-activated swarm node, and send a message using public key encryption. 100 | 101 | * E2_PssRouting.go 102 | 103 | Demonstrates how to perform dark routing in pss. 104 | 105 | * E3_PssSym.go 106 | 107 | Create an arbitrary symmetric encryption key, and send message with it. 108 | 109 | * E4_PssRaw.go 110 | 111 | Send a message using external encryption 112 | 113 | * E5_PssHandshake.go 114 | 115 | Using the builtin convenience method for Diffie-Hellmann key exchange. 116 | 117 | * E6_PssProtocol.go - **broken** 118 | 119 | Implementing devp2p style protocols over pss. 120 | 121 | * E7_PssClient.go - **broken** 122 | 123 | Mounting devp2p style protocols on an RPC connection. 124 | -------------------------------------------------------------------------------- /p2p/devp2p/common/defaults.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | const ( 8 | P2pPort = 30100 9 | IPCName = "demo.ipc" 10 | DatadirPrefix = ".data_" 11 | ) 12 | 13 | var ( 14 | basePath, _ = os.Getwd() 15 | ) 16 | -------------------------------------------------------------------------------- /p2p/devp2p/common/keyfile: -------------------------------------------------------------------------------- 1 | {"address":"faa28fe2e702c276e7de2d0f8e5e6b291a62431a","crypto":{"cipher":"aes-128-ctr","ciphertext":"106ec8a319c176710899a94804c7edefad1290a596ca655b8524fcfbcc2c0777","cipherparams":{"iv":"541e3da49efdcd99826a185bfd20ba77"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a108c173df0fac872242d7532579493a7ef1d9fb5312c142e75e14989d069452"},"mac":"2585168a39a23558a08ea882269d136d2eec8971fd4bdb7c69272eade8f5799e"},"id":"ab01a055-4fc7-4d61-b135-57fba922c33a","version":3} -------------------------------------------------------------------------------- /p2p/devp2p/common/protocol.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/ethereum/go-ethereum/p2p" 8 | "github.com/ethereum/go-ethereum/p2p/enode" 9 | "github.com/ethereum/go-ethereum/rpc" 10 | "github.com/ethersphere/swarm/p2p/protocols" 11 | ) 12 | 13 | const ( 14 | FooProtocolName = "fooping" 15 | FooProtocolVersion = 42 16 | FooProtocolMaxMsgSize = 1024 17 | ) 18 | 19 | type FooPingMsg struct { 20 | Pong bool 21 | Created time.Time 22 | } 23 | 24 | var ( 25 | FooMessages = []interface{}{ 26 | &FooPingMsg{}, 27 | } 28 | FooProtocol = protocols.Spec{ 29 | Name: FooProtocolName, 30 | Version: FooProtocolVersion, 31 | MaxMsgSize: FooProtocolMaxMsgSize, 32 | Messages: FooMessages, 33 | } 34 | ) 35 | 36 | // the service we want to offer on the node 37 | // it must implement the node.Service interface 38 | type FooService struct { 39 | pongcount int 40 | pingC map[enode.ID]chan struct{} 41 | } 42 | 43 | func NewFooService() *FooService { 44 | return &FooService{ 45 | pingC: make(map[enode.ID]chan struct{}), 46 | } 47 | } 48 | 49 | // specify API structs that carry the methods we want to use 50 | func (self *FooService) APIs() []rpc.API { 51 | return []rpc.API{ 52 | { 53 | Namespace: "foo", 54 | Version: "42", 55 | Service: NewFooAPI(self.pingC, &self.pongcount), 56 | Public: true, 57 | }, 58 | } 59 | } 60 | 61 | // the p2p.Protocol to run 62 | // sends a ping to its peer, waits pong 63 | func (self *FooService) Protocols() []p2p.Protocol { 64 | return []p2p.Protocol{ 65 | { 66 | Name: "fooping", 67 | Version: 666, 68 | Length: 1, 69 | Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 70 | 71 | // create the channel when a connection is made 72 | self.pingC[p.ID()] = make(chan struct{}) 73 | pingcount := 0 74 | 75 | // create the message structure 76 | 77 | // we don't know if we're awaiting anything at the time of the kill so this subroutine will run till the application ends 78 | go func() { 79 | for { 80 | Log.Debug("in pong catch") 81 | msg, err := rw.ReadMsg() 82 | if err != nil { 83 | Log.Warn("Receive p2p message fail", "err", err) 84 | break 85 | } 86 | 87 | Log.Debug("in pong catch after readmsg") 88 | // decode the message and check the contents 89 | var decodedmsg FooPingMsg 90 | err = msg.Decode(&decodedmsg) 91 | if err != nil { 92 | Log.Error("Decode p2p message fail", "err", err) 93 | break 94 | } 95 | 96 | // if we get a pong, update our pong counter 97 | if decodedmsg.Pong { 98 | self.pongcount++ 99 | Log.Debug("received pong", "peer", p, "count", self.pongcount) 100 | } else { 101 | Log.Debug("received ping", "peer", p) 102 | pingmsg := &FooPingMsg{ 103 | Pong: true, 104 | Created: time.Now(), 105 | } 106 | err := p2p.Send(rw, 0, pingmsg) 107 | if err != nil { 108 | Log.Error("Send p2p message fail", "err", err) 109 | break 110 | } 111 | Log.Debug("sent pong", "peer", p) 112 | } 113 | } 114 | }() 115 | 116 | // pings are invoked through the API using a channel 117 | // when this channel is closed we quit the protocol 118 | for { 119 | _, ok := <-self.pingC[p.ID()] 120 | if !ok { 121 | Log.Debug("break protocol", "peer", p) 122 | break 123 | } 124 | pingmsg := &FooPingMsg{ 125 | Pong: false, 126 | Created: time.Now(), 127 | } 128 | err := p2p.Send(rw, 0, pingmsg) 129 | if err != nil { 130 | return fmt.Errorf("Send p2p message fail: %v", err) 131 | } 132 | pingcount++ 133 | Log.Info("sending ping", "peer", p, "count", pingcount) 134 | } 135 | 136 | return nil 137 | }, 138 | }, 139 | } 140 | } 141 | 142 | func (self *FooService) Start(srv *p2p.Server) error { 143 | return nil 144 | } 145 | 146 | func (self *FooService) Stop() error { 147 | return nil 148 | } 149 | 150 | // Specify the API 151 | // in this example we don't care about who the pongs comes from, we count them all 152 | // note it is a bit fragile; we don't check for closed channels 153 | type FooAPI struct { 154 | running bool 155 | pongcount *int 156 | pingC map[enode.ID]chan struct{} 157 | } 158 | 159 | func NewFooAPI(pingC map[enode.ID]chan struct{}, pongcount *int) *FooAPI { 160 | return &FooAPI{ 161 | running: true, 162 | pingC: pingC, 163 | pongcount: pongcount, 164 | } 165 | } 166 | 167 | func (api *FooAPI) Increment() { 168 | *api.pongcount++ 169 | } 170 | 171 | // invoke a single ping 172 | func (api *FooAPI) Ping(id enode.ID) error { 173 | if api.running { 174 | api.pingC[id] <- struct{}{} 175 | } 176 | return nil 177 | } 178 | 179 | // quit the ping protocol 180 | func (api *FooAPI) Quit(id enode.ID) error { 181 | Log.Debug("quitting API", "peer", id) 182 | if api.pingC[id] == nil { 183 | return fmt.Errorf("unknown peer") 184 | } 185 | api.running = false 186 | close(api.pingC[id]) 187 | return nil 188 | } 189 | 190 | // return the amounts of pongs received 191 | func (api *FooAPI) PongCount() (int, error) { 192 | return *api.pongcount, nil 193 | } 194 | -------------------------------------------------------------------------------- /p2p/devp2p/doc/A1.latex: -------------------------------------------------------------------------------- 1 | \subsection{Server minimalism} 2 | The best way to learn is to remove all distractions, so let's stick with the bare-bear necessities first. 3 | In this example we merely create an \textit{instance} of the \gls{p2pserver}, \textit{start} it and stop it again. Not very useful, but it's the start of something great, promise! 4 | 5 | By \textit{starting} we mean that the \verb|UDP| and \verb|TCP| listener sockets are opened, upon which the server will: 6 | \begin{itemize} 7 | \item wait for connections 8 | \item be able to connect to other \glspl{nodep2p} 9 | \end{itemize} 10 | 11 | \begin{lstlisting}[caption=A1\_Server.go - Make a new private key,firstnumber=13] 12 | privkey, err := crypto.GenerateKey() 13 | if err != nil { 14 | demo.Log.Crit("Generate private key failed", "err", err) 15 | } 16 | \end{lstlisting} 17 | The first step is to create the \acrshort{node}'s \textit{private key}. The public key derived from it will be the public identity of the \acrshort{node}. Don't worry; the private key is generated by random, chosen from a range of numbers greater than the particle count of the entire universe. It will be quite unique. 18 | 19 | \begin{lstlisting}[caption=A1\_Server.go - Set up server,firstnumber=19] 20 | cfg := p2p.Config{ 21 | PrivateKey: privkey, 22 | Name: common.MakeName("foo", "42"), 23 | } 24 | srv := p2p.Server{ 25 | Config: cfg, 26 | } 27 | \end{lstlisting} 28 | The private key is passed to the server's configuration, along with an arbitrary \textit{name}, usually made up by an identifier describing its codebase, architecture and version number. 29 | The name has no practical significance in the scope of our tutorials, but it is a required field, and kind of polite to take seriously. 30 | There are plenty of other settings under the hood of the \gls{p2pserver}. Luckily, all of them will receive nice default values when we fire things up. 31 | 32 | \begin{lstlisting}[caption=A1\_Server.go - Start. Verify. Stop,firstnumber=27] 33 | // attempt to start the server 34 | err = srv.Start() 35 | if err != nil { 36 | demo.Log.Crit("Start p2p.Server failed", "err", err) 37 | } 38 | 39 | // inspect the resulting values 40 | nodeinfo := srv.NodeInfo() 41 | demo.Log.Info("server started", "enode", nodeinfo.Enode, "name", nodeinfo.Name, 42 | "ID", nodeinfo.ID, "IP", nodeinfo.IP) 43 | 44 | // bring down the server 45 | srv.Stop() 46 | \end{lstlisting} 47 | \textbf{The moment of truth.} 48 | If your computer doesn't blow up before then, you should see a log line in your console showing you some basic information about your running server, along the lines of\footnote{we will be truncating long hex strings in these tutorials. This is your last and final warning}: 49 | 50 | \scriptsize 51 | \begin{verbatim} 52 | INFO [06-24|22:12:49] server started demolog=* enode=enode://92f6fa82[\ldots]dc0912ba@[::]:46851 53 | name=foo/v42/linux/go1.8.3 ID=92f6fa82[\ldots]dc0912ba IP=:: caller=A1\_Server.go:32 54 | \end{verbatim} 55 | \normalsize 56 | 57 | An IP address has now been awarded to us, which spells good news for future connectivity. Fair enough, the IP field here merely cites the somewhat cryptic ``::'', but this is shorthand for \verb|localhost| so we're good. 58 | 59 | The two fields of special interest to us are the \texttt{ID} and the \texttt{\gls{enode}}. See how they contain nearly the same thing? Or rather, how the \texttt{\gls{enode}} contains the \texttt{ID} and a bit more? Here's how the entities we've met so far fit together: 60 | 61 | \begin{description} 62 | \item[server] Holds a private key and controls ip connections. 63 | \item[ID] The one-of-a-kind public key of the \gls{p2pserver}, derived from its private key (the one we made up in the start of the example). 64 | \item[enode] An uri-string that is used when you want to tell a \gls{p2pserver} who to connect to, made up of the server's ID (more on that next) along with the IP address and port number of the node. 65 | \end{description} 66 | Of course, every time a \gls{p2pserver} starts up it might have a different address or port, so we need to know about those to reach out to it at all. But we also want to make sure that the connection actually is for the \acrshort{node} in question, and not some other shady node that popped up on that address and port in the meantime. Therefore we also include the public key. And if the public key doesn't match, no soup for you. 67 | 68 | 69 | -------------------------------------------------------------------------------- /p2p/devp2p/doc/A2.latex: -------------------------------------------------------------------------------- 1 | \subsection{Getting hooked up} 2 | 3 | Now we bring up two of the servers as in the previous example and connect them. That is, connect one to the other. They act as consenting adults; the connection will be mutual, or none at all. 4 | 5 | \begin{lstlisting}[caption=A2\_Connect.go - Generic server creation,firstnumber=15] 6 | func newServer(privkey *ecdsa.PrivateKey, name string, version string, port int) *p2p.Server { 7 | 8 | // we need to explicitly allow at least one peer 9 | // otherwise the connection attempt will be refused 10 | cfg := p2p.Config{ 11 | PrivateKey: privkey, 12 | Name: common.MakeName(name, version), 13 | MaxPeers: 1, 14 | } 15 | if port > 0 { 16 | cfg.ListenAddr = fmt.Sprintf(":%d", port) 17 | } 18 | srv := &p2p.Server{ 19 | Config: cfg, 20 | } 21 | return srv 22 | } 23 | \end{lstlisting} 24 | 25 | The code is almost the same as before, except for two things: 26 | 27 | We are specifying the \emph{port} explicitly. It has a default value, but when we are setting up two servers on the same host we need different ports for them. Otherwise the OS will complain loudly. 28 | 29 | Also, with the \verb|MaxPeers| setting we're telling the server that it will allow one and only one peer to connect to it. Without this setting the value will be 0, which means the node will play very hard-to-get and any attempt of connecting will be coldly rejected. If you believe more is merrier, which is usually the case in p2p networks, this setting will usually be higher. 30 | \begin{lstlisting}[caption=A2\_Connect.go - Start servers,firstnumber=46] 31 | srv_one := newServer(privkey_one, "foo", "42", 0) 32 | err = srv_one.Start() 33 | if err != nil { 34 | demo.Log.Crit("Start p2p.Server #1 failed", "err", err) 35 | } 36 | 37 | srv_two := newServer(privkey_two, "bar", "666", 31234) 38 | err = srv_two.Start() 39 | if err != nil { 40 | demo.Log.Crit("Start p2p.Server #2 failed", "err", err) 41 | } 42 | \end{lstlisting} 43 | 44 | In the same manner as before, we start the two servers. We give the second different parameters for name, version and port. 45 | 46 | \begin{lstlisting}[caption=A2\_Connect.go - Connect,firstnumber=58] 47 | // get the node instance of the second server 48 | node_two := srv_two.Self() 49 | 50 | // add it as a peer to the first node 51 | // the connection and crypto handshake will be performed automatically 52 | srv_one.AddPeer(node_two) 53 | \end{lstlisting} 54 | 55 | The \verb|Self()| call on the server gives you back a \gls{discovernode} object. This object represents the server as a node, and contains all the information needed for a connection. So we can simply pass this \gls{discovernode} as an argument to the aptly-named \verb|AddPeer| method of the server. 56 | 57 | \begin{lstlisting}[caption=A2\_Connect.go - Check,firstnumber=65] 58 | // give the connection a glimpse of time to complete 59 | time.Sleep(time.Millisecond * 100) 60 | 61 | // inspect the results 62 | demo.Log.Info("after add", "node one peers", srv_one.Peers(), "node two peers", srv_two.Peers()) 63 | \end{lstlisting} 64 | 65 | The connection is created asynchronously, and might take a split second to complete. So if we rush into proving that we now have a shiny new peer in our connection list, we will most certainly be disappointed. So we give it pleeenty of time a.k.a. 100 milliseconds to complete. In the next section we'll learn a way more clever way to do this. Anyway you can rejoice for now: Connection is made. \textbf{Victory is ours}. 66 | 67 | \subsubsection{A note on nodes} 68 | The term \verb|node| used here is one of many notions of node in the go-ethereum code base, which at some point surely will confuse you. This flavor of node has to do with finding (discovering) nodes to connect to, and connect to them. And this specific object holds information specific to the connection, like IP information and an ID, or pretty much what makes up the \gls{enode} string we mentioned in the last section. See how it all neatly fits together? 69 | -------------------------------------------------------------------------------- /p2p/devp2p/doc/A3.latex: -------------------------------------------------------------------------------- 1 | \subsection{Know what's going on} 2 | 3 | So how to we get rid of that unaesthetic and unprecise timeout from the last example? We use the \glspl{peerevent}, of course. When connections start and stop, and when messages are sent and received, \glspl{peerevent} are emitted. 4 | 5 | \begin{lstlisting}[caption=A3\_Events.go - Subscribe to events,firstnumber=60] 6 | // set up the event subscription on the first server 7 | eventC := make(chan *p2p.PeerEvent) 8 | sub_one := srv_one.SubscribeEvents(eventC) 9 | \end{lstlisting} 10 | 11 | All you have to do is pass a channel to the \verb|SubscribeEvents| method of the \gls{p2pserver}, and you will start receiving them through it. 12 | 13 | \begin{lstlisting}[caption=A3\_Events.go - Receive events,firstnumber=64] 14 | // listen for events 15 | go func() { 16 | peerevent := <-eventC 17 | demo.Log.Info("received peerevent", "type", peerevent.Type, "peer", peerevent.Peer) 18 | quitC <- true 19 | }() 20 | \end{lstlisting} 21 | 22 | Then fork before the \verb|AddPeer| call, and listen on the channel for an event. Of course normally we would check what event it is, what peer it came from and so on and so forth. In this case, however, we're not really doing much, so we know that the event will be of type ``add'' and the peer will be the node we're \emph{not} subscribing to. 23 | 24 | \begin{lstlisting}[caption=p2p/peer.go - PeerEvents] 25 | // PeerEventType is the type of peer events emitted by a p2p.Server 26 | type PeerEventType string 27 | 28 | const ( 29 | // PeerEventTypeAdd is the type of event emitted when a peer is added 30 | // to a p2p.Server 31 | PeerEventTypeAdd PeerEventType = "add" 32 | 33 | // PeerEventTypeDrop is the type of event emitted when a peer is 34 | // dropped from a p2p.Server 35 | PeerEventTypeDrop PeerEventType = "drop" 36 | 37 | // PeerEventTypeMsgSend is the type of event emitted when a 38 | // message is successfully sent to a peer 39 | PeerEventTypeMsgSend PeerEventType = "msgsend" 40 | 41 | // PeerEventTypeMsgSend is the type of event emitted when a 42 | // message is successfully sent to a peer 43 | PeerEventTypeMsgRecv PeerEventType = "msgrecv" 44 | ) 45 | 46 | // PeerEvent is an event emitted when peers are either added or dropped from 47 | // a p2p.Server or when a message is sent or received on a peer connection 48 | type PeerEvent struct { 49 | Type PeerEventType `json:"type"` 50 | Peer discover.NodeID `json:"peer"` 51 | Error string `json:"error,omitempty"` 52 | MsgCode *uint64 `json:"msg_code,omitempty"` 53 | MsgSize *uint32 `json:"msg_size,omitempty"` 54 | } 55 | \end{lstlisting} 56 | 57 | A quick look in the \verb|peer.go| file in the \verb|p2p| package tells what kind of \glspl{peerevent} we have, and how they're stuctured. 58 | 59 | \begin{lstlisting}[caption=A3\_Events.go - Receive events,firstnumber=78] 60 | // receives when the event is received 61 | <-quitC 62 | 63 | // inspect the results 64 | demo.Log.Info("after add", "node one peers", srv_one.Peers(), "node two peers", srv_two.Peers()) 65 | 66 | // terminate subscription 67 | sub_one.Unsubscribe() 68 | \end{lstlisting} 69 | 70 | When the connection is completed (on the node we're subscribing to), we output the logline as before. Don't feel guilty about terminating the subscription after just one item. You'll get your money's worth later. 71 | -------------------------------------------------------------------------------- /p2p/devp2p/doc/article.latex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper,dvipsnames]{article} 2 | \usepackage{tcolorbox} 3 | \usepackage{hyperref} 4 | \usepackage{fullpage} 5 | \usepackage{comment} 6 | \usepackage{listings} 7 | \usepackage[autostyle]{csquotes} 8 | \usepackage{listings-golang} 9 | \usepackage{glossaries} 10 | \lstset{ 11 | basicstyle=\footnotesize, 12 | numbers=left, 13 | numberstyle=\tiny, 14 | showstringspaces=false, 15 | keywordstyle=\color{Purple}, 16 | stringstyle=\color{ForestGreen}, 17 | commentstyle=\color{Gray}, 18 | language=Golang, 19 | } 20 | \newglossaryentry{peerevent} 21 | { 22 | name={peer-event}, 23 | description={}, 24 | plural={peer-events} 25 | } 26 | \newglossaryentry{discovernode} 27 | { 28 | name={discovery node}, 29 | description={}, 30 | plural={discovery nodes} 31 | } 32 | \newglossaryentry{ethnode} 33 | { 34 | name={ethereum node}, 35 | description={}, 36 | plural={ethereum nodes} 37 | } 38 | \newglossaryentry{nodep2p} 39 | { 40 | name={p2p node}, 41 | description={ 42 | }, 43 | plural={p2p nodes} 44 | 45 | } 46 | \newglossaryentry{rlp} 47 | { 48 | name={rlp}, 49 | description={ 50 | } 51 | } 52 | \newglossaryentry{rlpx} 53 | { 54 | name={rlpx}, 55 | description={\gls{rlp} 56 | } 57 | } 58 | \newglossaryentry{nodeservice} 59 | { 60 | name={node service}, 61 | description={ 62 | }, 63 | plural={node services} 64 | } 65 | \newglossaryentry{servicenode} 66 | { 67 | name={sevicenode}, 68 | description={A code package that serves one or more \glspl{nodeservice} to a \gls{nodep2p} over an \gls{rlpx}-encoded tcp/ip connection. 69 | } 70 | } 71 | \newglossaryentry{enode} 72 | { 73 | name={enode}, 74 | description={a uri scheme for an ethereum \gls{servicenode}, preceded by \verb|enode://|. The data part is comprised of its \textit{public id} and \gls{host string} separated by '@'. 75 | } 76 | } 77 | \newglossaryentry{p2pserver} 78 | { 79 | name={devp2p server}, 80 | description={some text 81 | some more text 82 | } 83 | } 84 | \newglossaryentry{devp2p} 85 | { 86 | name={devp2p}, 87 | description={Popular name for the lowest level of peer-to-peer communication in the go-ethereum codebase.} 88 | } 89 | \makeglossaries 90 | \newacronym[see={[Glossary:]{servicenode}}]{node}{node}{servicenode} 91 | \author{lash} 92 | \begin{document} 93 | \title{getting started with devp2p} 94 | \date{\today\\ v0.2} 95 | \maketitle 96 | 97 | \section*{Introduction} 98 | \begin{tcolorbox}[colback=Lavender,colframe=Red,arc=0mm] 99 | This document is work in progress. In the first version deemed fit for release, this pink box will no longer be here. 100 | \end{tcolorbox} 101 | These tutorials show you how to make \glspl{ethnode} communicate with each other using the \verb|go-ethereum| code base. They do \emph{not} show you \emph{how} to program, nor does it provide entry-level familiarity to the \verb|golang| programming language, which \verb|go-ethereum| is written in. 102 | 103 | For the latter I can warmly recommend taking the official ``go tour'', but be warned; although \verb|golang| aims at providing friendly access to fairly low-level operations, it's still fairly low-level. So if you come from the less cumbersome environment of web-scripting and such, you're probably in for a challenge. 104 | \newline 105 | 106 | The Ethersphere, like its crypto comrades, dabbles quite a bit in moving individual bytes around before crunching them with big number math. However, most of the code we shall see fortunately manages to steer clear of that, and concentrates on some nice, magic function calls that spares us a few pints of elbow grease. 107 | 108 | The meaty parts of this document are code examples that all centre on runnable mini-applications that reveal features and structure in small incremental steps. And they are even grouped in themes that incrementally add to the complexity of components of the code stack that we will be using in everyday p2p life. 109 | 110 | In the current version of this document, the layers we will peel are as follows: 111 | \begin{enumerate} 112 | \item \textbf{The \gls{p2pserver}}; connections and messages 113 | \item \textbf{Remote Procedure Calls}; defining and using APIs 114 | \item \textbf{Services}; bringing protocols and APIs together 115 | \item \textbf{Pss}: relay messages across peer-to-peer connections 116 | \end{enumerate} 117 | \section{p2p in Ethereum} 118 | [\ldots] 119 | \section{the code examples} 120 | % insert articles here 121 | \input{A1.latex} 122 | \input{A2.latex} 123 | \input{A3.latex} 124 | \input{A4.latex} 125 | \end{document} 126 | -------------------------------------------------------------------------------- /p2p/devp2p/doc/listings-golang.sty: -------------------------------------------------------------------------------- 1 | %% Golang definition for listings 2 | %% http://github.io/julienc91/lstlistings-golang 3 | %% 4 | \RequirePackage{listings} 5 | 6 | \lstdefinelanguage{Golang}% 7 | {morekeywords=[1]{package,import,func,type,struct,return,defer,panic,% 8 | recover,select,var,const,iota,},% 9 | morekeywords=[2]{string,uint,uint8,uint16,uint32,uint64,int,int8,int16,% 10 | int32,int64,bool,float32,float64,complex64,complex128,byte,rune,uintptr,% 11 | error,interface},% 12 | morekeywords=[3]{map,slice,make,new,nil,len,cap,copy,close,true,false,% 13 | delete,append,real,imag,complex,chan,},% 14 | morekeywords=[4]{for,break,continue,range,goto,switch,case,fallthrough,if,% 15 | else,default,},% 16 | morekeywords=[5]{Println,Printf,Error,},% 17 | sensitive=true,% 18 | morecomment=[l]{//},% 19 | morecomment=[s]{/*}{*/},% 20 | morestring=[b]',% 21 | morestring=[b]",% 22 | morestring=[s]{`}{`},% 23 | } 24 | -------------------------------------------------------------------------------- /p2p/devp2p/runall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for f in *.go; do 4 | go run $f 5 | res=$? 6 | if [ $res -gt 0 ]; then 7 | echo $f returned $res 8 | exit 1 9 | fi 10 | done 11 | -------------------------------------------------------------------------------- /p2p/protocol-complex/.service_old/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/log" 9 | "github.com/ethereum/go-ethereum/p2p" 10 | "github.com/ethereum/go-ethereum/p2p/protocols" 11 | "github.com/ethereum/go-ethereum/rpc" 12 | "github.com/ethereum/go-ethereum/swarm/pss" 13 | ) 14 | 15 | type DemoService struct { 16 | enodes []string 17 | protocol *pss.Protocol 18 | } 19 | 20 | // implement node.Service 21 | func (self *DemoService) Protocols() []p2p.Protocol { 22 | return []p2p.Protocol{ 23 | { 24 | Name: "demo", 25 | Version: 1, 26 | Length: 1, 27 | Run: self.Run, 28 | }, 29 | } 30 | } 31 | 32 | func (self *DemoService) APIs() []rpc.API { 33 | return []rpc.API{ 34 | { 35 | Namespace: "demo", 36 | Version: "1.0", 37 | Service: newDemoServiceAPI(self.protocol, self.Run), 38 | Public: true, 39 | }, 40 | } 41 | } 42 | 43 | func (self *DemoService) Start(p *p2p.Server) error { 44 | return nil 45 | } 46 | 47 | func (self *DemoService) Stop() error { 48 | return nil 49 | } 50 | 51 | // Implement rest of PssService 52 | func (self *DemoService) Spec() *protocols.Spec { 53 | return &protocols.Spec{ 54 | Name: protoName, 55 | Version: protoVersion, 56 | MaxMsgSize: protoMax, 57 | Messages: []interface{}{ 58 | &Result{}, 59 | &Status{}, 60 | &Job{}, 61 | }, 62 | } 63 | } 64 | 65 | func (self *DemoService) Topic() *pss.Topic { 66 | return &protoTopic 67 | } 68 | 69 | func (self *DemoService) Init(ps *pss.Pss) error { 70 | protocol := self.Protocols()[0] 71 | psp, err := pss.RegisterProtocol(ps, self.Topic(), self.Spec(), &protocol, &pss.ProtocolParams{true, true}) 72 | if err != nil { 73 | return err 74 | } 75 | ps.Register(self.Topic(), psp.Handle) 76 | self.protocol = psp 77 | return nil 78 | } 79 | 80 | func (self *DemoService) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error { 81 | pp := protocols.NewPeer(p, rw, self.Spec()) 82 | go func() { 83 | err := pp.Send(&Status{true}) 84 | log.Error("send fail", "peer", pp, "err", err) 85 | }() 86 | 87 | err := pp.Run(self.handle) 88 | return err 89 | } 90 | 91 | func (self *DemoService) handle(msg interface{}) error { 92 | log.Info("have msg", "msg", msg) 93 | return nil 94 | } 95 | 96 | // api to interact with pss protocol 97 | type DemoServiceAPI struct { 98 | protocol *pss.Protocol 99 | run func(*p2p.Peer, p2p.MsgReadWriter) error 100 | } 101 | 102 | func newDemoServiceAPI(prot *pss.Protocol, run func(*p2p.Peer, p2p.MsgReadWriter) error) *DemoServiceAPI { 103 | return &DemoServiceAPI{ 104 | protocol: prot, 105 | run: run, 106 | } 107 | } 108 | 109 | func (self *DemoServiceAPI) AddPssPeer(key string) error { 110 | self.protocol.AddPeer(&p2p.Peer{}, self.run, protoTopic, true, key) 111 | log.Info(fmt.Sprintf("adding peer %x to demoservice protocol", key)) 112 | return nil 113 | } 114 | -------------------------------------------------------------------------------- /p2p/protocol-complex/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.10-alpine 2 | 3 | WORKDIR /home/bzz 4 | 5 | # ENV GOPATH /home/bzz 6 | 7 | COPY . /home/bzz 8 | 9 | RUN apk add --update git bash gcc musl-dev linux-headers 10 | 11 | RUN mkdir -p $GOPATH/src/github.com/ethereum && \ 12 | cd $GOPATH/src/github.com/ethereum && \ 13 | git clone https://github.com/nolash/go-ethereum && \ 14 | cd go-ethereum && \ 15 | git checkout sos18-demo-resource && \ 16 | cd /home/bzz && \ 17 | go build -o main main.go 18 | 19 | CMD [ "bash" ] 20 | -------------------------------------------------------------------------------- /p2p/protocol-complex/README.md: -------------------------------------------------------------------------------- 1 | # sample pss protocol 2 | 3 | **support for "mutable resources" is broken due to change to swarm feeds API. needs fix** 4 | 5 | This example illustrates how to implement a protocol of some complexity using `pss`. 6 | 7 | The `sim.go` driver implements the protocol on a normal `devp2p` connection using the simulations framework. 8 | 9 | The `sim_pss.go` driver implements the same protocol on over `pss` using the simulations framework. 10 | 11 | The `main.go` and `main_pss.go` files are respective standalone binaries . 12 | 13 | Files in `service/` and `protocol/` implement the protocol itself, and are shared between both drivers. The pss and swarm specific code is isolated to `bzz/`. This way, the extra implmentation needed for `pss` is hopefully clear. 14 | 15 | -------------------------------------------------------------------------------- /p2p/protocol-complex/contracts/pss.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | contract pss { 4 | mapping (address => uint256) deposits; 5 | mapping (address => uint256) credits; 6 | 7 | // custom functions 8 | function deposit() public payable { 9 | deposits[msg.sender] += msg.value; 10 | } 11 | 12 | function transfer(uint256 amount, address beneficiary) public { 13 | require(deposits[msg.sender] >= amount); 14 | deposits[msg.sender] -= amount; 15 | credits[beneficiary] += amount; 16 | } 17 | 18 | function withdraw(uint256 amount) public { 19 | require(credits[msg.sender] >= amount); 20 | 21 | credits[msg.sender] -= amount; 22 | if (!msg.sender.send(amount)) { 23 | credits[msg.sender] += amount; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /p2p/protocol-complex/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "os/signal" 9 | "strconv" 10 | "strings" 11 | "syscall" 12 | "time" 13 | 14 | "github.com/ethereum/go-ethereum/log" 15 | "github.com/ethereum/go-ethereum/node" 16 | 17 | "./service" 18 | ) 19 | 20 | const ( 21 | ipcName = "pssdemo.ipc" 22 | defaultMaxDifficulty = 23 23 | defaultMaxJobs = 3 24 | defaultMaxTime = time.Second 25 | ) 26 | 27 | var ( 28 | loglevel = flag.Int("l", 3, "loglevel") 29 | port = flag.Int("p", 30499, "p2p port") 30 | bzzport = flag.String("b", "8555", "bzz port") 31 | enode = flag.String("e", "", "enode to connect to") 32 | httpapi = flag.String("a", "localhost:8545", "http api") 33 | ) 34 | 35 | func init() { 36 | flag.Parse() 37 | log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(*loglevel), (log.StreamHandler(os.Stderr, log.TerminalFormat(true)))))) 38 | } 39 | 40 | func main() { 41 | 42 | datadir, err := ioutil.TempDir("", "pssmailboxdemo-") 43 | if err != nil { 44 | log.Error("dir create fail", "err", err) 45 | return 46 | } 47 | defer os.RemoveAll(datadir) 48 | 49 | cfg := &node.DefaultConfig 50 | cfg.P2P.ListenAddr = fmt.Sprintf(":%d", *port) 51 | cfg.P2P.EnableMsgEvents = true 52 | cfg.IPCPath = ipcName 53 | 54 | httpspec := strings.Split(*httpapi, ":") 55 | httpport, err := strconv.ParseInt(httpspec[1], 10, 0) 56 | if err != nil { 57 | log.Error("node create fail", "err", err) 58 | return 59 | } 60 | 61 | if *httpapi != "" { 62 | cfg.HTTPHost = httpspec[0] 63 | cfg.HTTPPort = int(httpport) 64 | cfg.HTTPModules = []string{"demo", "admin", "pss"} 65 | } 66 | cfg.DataDir = datadir 67 | 68 | stack, err := node.New(cfg) 69 | if err != nil { 70 | log.Error("node create fail", "err", err) 71 | return 72 | } 73 | 74 | // create the demo service and register it with the node stack 75 | if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { 76 | params := service.NewDemoParams(nil, nil) 77 | params.MaxJobs = defaultMaxJobs 78 | params.MaxTimePerJob = defaultMaxTime 79 | params.MaxDifficulty = defaultMaxDifficulty 80 | return service.NewDemo(params) 81 | }); err != nil { 82 | log.Error(err.Error()) 83 | return 84 | } 85 | if err := stack.Start(); err != nil { 86 | log.Error(err.Error()) 87 | return 88 | } 89 | defer stack.Stop() 90 | sigC := make(chan os.Signal) 91 | signal.Notify(sigC, syscall.SIGINT) 92 | 93 | <-sigC 94 | } 95 | -------------------------------------------------------------------------------- /p2p/protocol-complex/main_pss.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "os/signal" 9 | "strconv" 10 | "strings" 11 | "syscall" 12 | "time" 13 | 14 | "github.com/ethereum/go-ethereum/crypto" 15 | "github.com/ethereum/go-ethereum/log" 16 | "github.com/ethereum/go-ethereum/node" 17 | swarmapi "github.com/ethereum/go-ethereum/swarm/api" 18 | 19 | "./bzz" 20 | "./service" 21 | ) 22 | 23 | const ( 24 | ipcName = "pssdemo.ipc" 25 | defaultMaxDifficulty = 23 26 | defaultMaxJobs = 3 27 | defaultMaxTime = time.Second 28 | ) 29 | 30 | var ( 31 | loglevel = flag.Int("l", 3, "loglevel") 32 | port = flag.Int("p", 30499, "p2p port") 33 | bzzport = flag.String("b", "8555", "bzz port") 34 | enode = flag.String("e", "", "enode to connect to") 35 | httpapi = flag.String("a", "localhost:8545", "http api") 36 | ) 37 | 38 | func init() { 39 | flag.Parse() 40 | log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(*loglevel), (log.StreamHandler(os.Stderr, log.TerminalFormat(true)))))) 41 | } 42 | 43 | func main() { 44 | 45 | datadir, err := ioutil.TempDir("", "pssmailboxdemo-") 46 | if err != nil { 47 | log.Error("dir create fail", "err", err) 48 | return 49 | } 50 | defer os.RemoveAll(datadir) 51 | 52 | cfg := &node.DefaultConfig 53 | cfg.P2P.ListenAddr = fmt.Sprintf(":%d", *port) 54 | cfg.P2P.EnableMsgEvents = true 55 | cfg.IPCPath = ipcName 56 | 57 | httpspec := strings.Split(*httpapi, ":") 58 | httpport, err := strconv.ParseInt(httpspec[1], 10, 0) 59 | if err != nil { 60 | log.Error("node create fail", "err", err) 61 | return 62 | } 63 | 64 | if *httpapi != "" { 65 | cfg.HTTPHost = httpspec[0] 66 | cfg.HTTPPort = int(httpport) 67 | cfg.HTTPModules = []string{"demo", "admin", "pss"} 68 | } 69 | cfg.DataDir = datadir 70 | 71 | stack, err := node.New(cfg) 72 | if err != nil { 73 | log.Error("node create fail", "err", err) 74 | return 75 | } 76 | 77 | // create the demo service, but now we don't register it directly 78 | // so we avoid the protocol running on the direct connected peers 79 | params := service.NewDemoParams(nil, nil) 80 | params.MaxJobs = defaultMaxJobs 81 | params.MaxTimePerJob = defaultMaxTime 82 | params.MaxDifficulty = defaultMaxDifficulty 83 | svc, err := service.NewDemo(params) 84 | if err != nil { 85 | log.Error(err.Error()) 86 | return 87 | } 88 | 89 | // create the pss service that wraps the demo protocol 90 | privkey, err := crypto.GenerateKey() 91 | if err != nil { 92 | log.Error(err.Error()) 93 | return 94 | } 95 | 96 | bzzCfg := swarmapi.NewConfig() 97 | bzzCfg.SyncEnabled = false 98 | bzzCfg.Port = *bzzport 99 | bzzCfg.Path = datadir 100 | bzzCfg.HiveParams.Discovery = true 101 | bzzCfg.Init(privkey) 102 | 103 | bzzSvc, err := bzz.NewBzzService(bzzCfg) 104 | if err != nil { 105 | log.Error(err.Error()) 106 | return 107 | } 108 | err = bzzSvc.RegisterPssProtocol(svc) 109 | if err != nil { 110 | log.Error(err.Error()) 111 | return 112 | } 113 | 114 | if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { 115 | return bzzSvc, nil 116 | }); err != nil { 117 | log.Error(err.Error()) 118 | return 119 | } 120 | 121 | if err := stack.Start(); err != nil { 122 | log.Error(err.Error()) 123 | return 124 | } 125 | defer stack.Stop() 126 | sigC := make(chan os.Signal) 127 | signal.Notify(sigC, syscall.SIGINT) 128 | <-sigC 129 | } 130 | -------------------------------------------------------------------------------- /p2p/protocol-complex/protocol/peer.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/ethereum/go-ethereum/p2p/protocols" 8 | ) 9 | 10 | // Every protocol that wants to send messages back to sender, must have a peer abstraction 11 | // this is because devp2p doesn't let us know about which peer is the sender 12 | type DemoPeer struct { 13 | *protocols.Peer 14 | skillsHandler func(*Skills, *protocols.Peer) error 15 | statusHandler func(*Status, *protocols.Peer) error 16 | requestHandler func(*Request, *protocols.Peer) error 17 | resultHandler func(*Result, *protocols.Peer) error 18 | } 19 | 20 | // Dispatcher for incoming messages 21 | func (self *DemoPeer) Handle(ctx context.Context, msg interface{}) error { 22 | if typ, ok := msg.(*Skills); ok { 23 | return self.skillsHandler(typ, self.Peer) 24 | } 25 | if typ, ok := msg.(*Status); ok { 26 | return self.statusHandler(typ, self.Peer) 27 | } 28 | if typ, ok := msg.(*Request); ok { 29 | return self.requestHandler(typ, self.Peer) 30 | } 31 | if typ, ok := msg.(*Result); ok { 32 | return self.resultHandler(typ, self.Peer) 33 | } 34 | return errors.New("unknown message type") 35 | } 36 | -------------------------------------------------------------------------------- /p2p/protocol-complex/protocol/protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/ethereum/go-ethereum/log" 7 | "github.com/ethereum/go-ethereum/p2p" 8 | "github.com/ethereum/go-ethereum/p2p/protocols" 9 | ) 10 | 11 | // enumeration used in status messages 12 | const ( 13 | StatusThanksABunch = iota 14 | StatusBusy 15 | StatusAreYouKidding 16 | StatusGaveup 17 | ) 18 | 19 | // which hashes a hasher node offers 20 | const ( 21 | HashSHA1 = iota 22 | ) 23 | 24 | // variables shared between p2p.Protocol and protocols.Spec 25 | const ( 26 | protoName = "demo" 27 | protoVersion = 1 28 | protoMax = 2048 29 | ) 30 | 31 | type ID [8]byte 32 | 33 | // Skills is a protocol message type 34 | // 35 | // It is an asynchronous handshake message, signaling the state the node is in. 36 | // The message may be issued at any time, and receiving peers should behave accordingly 37 | // towards the node: 38 | // 39 | // Difficulty > 0 means it's open for hashing, and what the max difficulty is. 40 | // 41 | // MaxSize tells how many bytes can accompany one data submission 42 | type Skills struct { 43 | Difficulty uint8 44 | MaxSize uint16 45 | } 46 | 47 | // Status is a protocol message type 48 | // 49 | // It is mostly used to signal errors states. 50 | // 51 | // A node also uses Status to signal successful reception of a Result message 52 | type Status struct { 53 | Id ID 54 | Code uint8 55 | } 56 | 57 | // Request is a protocol message type 58 | // 59 | // It is used by nodes to request a hashing job 60 | type Request struct { 61 | Id ID 62 | Data []byte 63 | Difficulty uint8 64 | } 65 | 66 | // Result is a protocol message type 67 | // 68 | // It is used by nodes to transmit the results of a hashing job 69 | type Result struct { 70 | Id ID 71 | Nonce []byte 72 | Hash []byte 73 | } 74 | 75 | var ( 76 | Messages = []interface{}{ 77 | &Skills{}, 78 | &Status{}, 79 | &Request{}, 80 | &Result{}, 81 | } 82 | 83 | Spec = &protocols.Spec{ 84 | Name: protoName, 85 | Version: protoVersion, 86 | MaxMsgSize: protoMax, 87 | Messages: Messages, 88 | } 89 | ) 90 | 91 | // The protocol object wraps the code that starts a protocol on a peer upon connection 92 | // 93 | // This implementation holds a callback function thats called upon a successful connection 94 | // Any logic needed to be performed in the context of the protocol's service should be put there 95 | type DemoProtocol struct { 96 | Protocol p2p.Protocol 97 | SkillsHandler func(*Skills, *protocols.Peer) error 98 | StatusHandler func(*Status, *protocols.Peer) error 99 | RequestHandler func(*Request, *protocols.Peer) error 100 | ResultHandler func(*Result, *protocols.Peer) error 101 | handler func(interface{}) error 102 | runHook func(*protocols.Peer) error 103 | } 104 | 105 | func NewDemoProtocol(runHook func(*protocols.Peer) error) (*DemoProtocol, error) { 106 | proto := &DemoProtocol{ 107 | Protocol: p2p.Protocol{ 108 | Name: protoName, 109 | Version: protoVersion, 110 | Length: 4, 111 | }, 112 | runHook: runHook, 113 | } 114 | 115 | return proto, nil 116 | } 117 | 118 | // TODO: double-check if we need the Init detached 119 | func (self *DemoProtocol) Init() error { 120 | if self.SkillsHandler == nil { 121 | return errors.New("missing skills handler") 122 | } 123 | if self.StatusHandler == nil { 124 | return errors.New("missing status handler") 125 | } 126 | if self.RequestHandler == nil { 127 | return errors.New("missing request handler") 128 | } 129 | if self.ResultHandler == nil { 130 | return errors.New("missing response handler") 131 | } 132 | self.Protocol.Run = self.Run 133 | return nil 134 | } 135 | 136 | // This method is run on every new peer connection 137 | // 138 | // It enters a loop that takes care of dispatching and receiving messages 139 | func (self *DemoProtocol) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error { 140 | pp := protocols.NewPeer(p, rw, Spec) 141 | log.Info("running demo protocol on peer", "peer", pp, "self", self) 142 | go self.runHook(pp) 143 | dp := &DemoPeer{ 144 | Peer: pp, 145 | skillsHandler: self.SkillsHandler, 146 | statusHandler: self.StatusHandler, 147 | requestHandler: self.RequestHandler, 148 | resultHandler: self.ResultHandler, 149 | } 150 | return pp.Run(dp.Handle) 151 | } 152 | -------------------------------------------------------------------------------- /p2p/protocol-complex/resource/README.md: -------------------------------------------------------------------------------- 1 | Resource is broken, needs upgrade to swarm feeds API 2 | -------------------------------------------------------------------------------- /p2p/protocol-complex/resource/resource.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/ethereum/go-ethereum/log" 9 | 10 | "../protocol" 11 | ) 12 | 13 | type Client struct { 14 | url string 15 | resource string 16 | client *http.Client 17 | ready bool 18 | } 19 | 20 | func NewClient(bzzapi string, resource string) *Client { 21 | return &Client{ 22 | client: http.DefaultClient, 23 | resource: resource, 24 | url: bzzapi, 25 | } 26 | } 27 | func (b *Client) createResource(data []byte) error { 28 | _, err := b.client.Post( 29 | fmt.Sprintf("%s/bzz-resource:/%s/raw/2", b.url, b.resource), 30 | "contenxt-type: application/octet-stream", 31 | bytes.NewBuffer(data), 32 | ) 33 | if err == nil { 34 | log.Debug("creating resource", "id", b.resource) 35 | b.ready = true 36 | } 37 | return err 38 | } 39 | 40 | func (b *Client) updateResource(data []byte) error { 41 | if !b.ready { 42 | return b.createResource(data) 43 | } 44 | _, err := b.client.Post( 45 | fmt.Sprintf("%s/bzz-resource:/%s/raw", b.url, b.resource), 46 | "content-type: application/octet-stream", 47 | bytes.NewBuffer(data), 48 | ) 49 | return err 50 | } 51 | 52 | func (b *Client) ResourceSinkFunc() func(interface{}) { 53 | return func(obj interface{}) { 54 | if res, ok := obj.(*protocol.Result); ok { 55 | log.Debug("posting", "obj", fmt.Sprintf("%x", res.Hash)) 56 | if err := b.updateResource(res.Hash); err != nil { 57 | log.Error("resource fail", "err", err, "hash", res.Hash) 58 | } 59 | } 60 | } 61 | } 62 | func main() { 63 | fmt.Println("vim-go") 64 | } 65 | -------------------------------------------------------------------------------- /p2p/protocol-complex/service/api.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "../protocol" 5 | ) 6 | 7 | type DemoAPI struct { 8 | service *Demo 9 | } 10 | 11 | func newDemoAPI(s *Demo) *DemoAPI { 12 | return &DemoAPI{ 13 | service: s, 14 | } 15 | } 16 | 17 | func (self *DemoAPI) Submit(data []byte, difficulty uint8) (protocol.ID, error) { 18 | return self.service.submitRequest(data, difficulty) 19 | } 20 | 21 | func (self *DemoAPI) Stop() error { 22 | //self.service.running = false 23 | return nil 24 | } 25 | 26 | func (self *DemoAPI) SetDifficulty(d uint8) error { 27 | self.service.mu.Lock() 28 | defer self.service.mu.Unlock() 29 | self.service.maxDifficulty = d 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /p2p/protocol-complex/service/minipow/check.go: -------------------------------------------------------------------------------- 1 | package minipow 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha1" 6 | ) 7 | 8 | // data does NOT include nonce here 9 | func Check(hash []byte, data []byte, nonce []byte) bool { 10 | h := sha1.New() 11 | h.Write(data) 12 | h.Write(nonce) 13 | return bytes.Equal(hash, h.Sum(nil)) 14 | } 15 | -------------------------------------------------------------------------------- /p2p/protocol-complex/service/minipow/minipow.go: -------------------------------------------------------------------------------- 1 | package minipow 2 | 3 | import ( 4 | "crypto/sha1" 5 | ) 6 | 7 | // TODO: implement a hasher pool for efficiency and concurrency 8 | // last 8 bytes of data holds the nonce 9 | // must be zero - the routine will NOT check 10 | func Mine(data []byte, difficulty int, resultC chan<- []byte, quitC <-chan struct{}, debug func([]byte, []byte)) { 11 | h := sha1.New() 12 | 13 | datalen := len(data) 14 | hashsizeminusone := h.Size() - 1 15 | diffbytes := make([]byte, h.Size()) 16 | result := make([]byte, h.Size()) 17 | 18 | // generate the difficulty mask 19 | var register byte 20 | c := 0 21 | OUTER_ONE: 22 | for i := hashsizeminusone; i > 0; i-- { 23 | register = 0x01 24 | for j := 1; j < 256; j *= 2 { 25 | diffbytes[i] |= register 26 | register <<= 1 27 | c++ 28 | if c == difficulty { 29 | break OUTER_ONE 30 | } 31 | } 32 | } 33 | 34 | diffthreshold := hashsizeminusone - int(difficulty/8) 35 | 36 | // 256 bit number is pretty close to eternity 37 | OUTER_TWO: 38 | for { 39 | // timeout handling 40 | select { 41 | case <-quitC: 42 | break OUTER_TWO 43 | default: 44 | } 45 | 46 | // increment the nonce 47 | for i := datalen - 1; i >= 0; i-- { 48 | data[i]++ 49 | if data[i]|0x0 != 0 { 50 | break 51 | } 52 | } 53 | 54 | // hashhhhh 55 | h.Reset() 56 | h.Write(data) 57 | sum := h.Sum(nil) 58 | if debug != nil { 59 | debug(data, sum) 60 | } 61 | copy(result[:], sum) 62 | 63 | // and byte for byte with the difficulty mask 64 | // we only check the bytes we have to, though, until diffthreshold 65 | // if not 0, we failed miserably 66 | for i := hashsizeminusone; i >= diffthreshold; i-- { 67 | sum[i] &= diffbytes[i] 68 | if sum[i] != 0x0 { 69 | continue OUTER_TWO 70 | } 71 | } 72 | 73 | // there was rejoicing 74 | resultC <- result 75 | return 76 | } 77 | resultC <- nil 78 | } 79 | -------------------------------------------------------------------------------- /p2p/protocol-complex/service/result.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | 8 | "../protocol" 9 | ) 10 | 11 | const ( 12 | defaultResultsCapacity = 1000 13 | defaultResultsReleaseDelay = time.Second * 2 14 | ) 15 | 16 | type ResultSinkFunc func(data interface{}) 17 | 18 | type resultEntry struct { 19 | *protocol.Result 20 | prid protocol.ID // was result.ID? 21 | expires time.Time 22 | } 23 | 24 | // TODO: revert to normal map instead of sync.Map 25 | type resultStore struct { 26 | // handle results 27 | entries []*resultEntry // hashing nodes store the results here, while awaiting ack of reception by requester 28 | idx sync.Map // index to look up resultentry by 29 | counter int // amount of results stored in resultsCounter 30 | capacity int // amount of results possible to store 31 | releaseDelay time.Duration // time before a result expires and should be passed to sinkFunc 32 | sinkFunc ResultSinkFunc // callback to pass data to when result has expired 33 | 34 | mu sync.RWMutex 35 | ctx context.Context 36 | } 37 | 38 | func newResultStore(ctx context.Context, sinkFunc ResultSinkFunc) *resultStore { 39 | return &resultStore{ 40 | entries: make([]*resultEntry, defaultResultsCapacity), 41 | //idx: make(map[protocol.ID]int), 42 | releaseDelay: defaultResultsReleaseDelay, 43 | capacity: defaultResultsCapacity, 44 | sinkFunc: sinkFunc, 45 | ctx: ctx, 46 | } 47 | } 48 | 49 | func (self *resultStore) Put(id protocol.ID, res *protocol.Result) bool { 50 | self.mu.Lock() 51 | defer self.mu.Unlock() 52 | if self.full() { 53 | return false 54 | } 55 | self.entries[self.counter] = &resultEntry{ 56 | Result: res, 57 | prid: id, 58 | expires: time.Now().Add(self.releaseDelay), 59 | } 60 | self.idx.Store(id, self.counter) 61 | self.counter++ 62 | return true 63 | } 64 | 65 | func (self *resultStore) Get(id protocol.ID) *protocol.Result { 66 | self.mu.RLock() 67 | defer self.mu.RUnlock() 68 | n, ok := self.idx.Load(id) 69 | if !ok { 70 | return nil 71 | } 72 | return self.entries[n.(int)].Result 73 | } 74 | 75 | func (self *resultStore) Del(id protocol.ID) { 76 | self.mu.Lock() 77 | defer self.mu.Unlock() 78 | self.del(id) 79 | } 80 | 81 | func (self *resultStore) del(id protocol.ID) { 82 | if n, ok := self.idx.Load(id); ok { 83 | self.idx.Delete(id) 84 | if self.counter == 0 { // reaches negative count unless this check, why? 85 | return 86 | } 87 | self.counter-- 88 | if self.counter > 0 { 89 | self.entries[n.(int)] = self.entries[self.counter] 90 | self.idx.Store(self.entries[n.(int)].prid, n.(int)) 91 | } 92 | } 93 | } 94 | 95 | func (self *resultStore) Count() int { 96 | self.mu.RLock() 97 | defer self.mu.RUnlock() 98 | return self.counter 99 | } 100 | 101 | func (self *resultStore) IsFull() bool { 102 | self.mu.RLock() 103 | defer self.mu.RUnlock() 104 | return self.full() 105 | } 106 | 107 | func (self *resultStore) full() bool { 108 | return self.counter == self.capacity 109 | } 110 | 111 | func (self *resultStore) Start() { 112 | go func() { 113 | for { 114 | timer := time.NewTimer(self.releaseDelay) 115 | select { 116 | case <-self.ctx.Done(): 117 | timer.Stop() 118 | return 119 | case <-timer.C: 120 | } 121 | self.prune() 122 | } 123 | }() 124 | } 125 | 126 | // TODO: this procedure needs priority control, so it doesn't block for too long 127 | func (self *resultStore) prune() { 128 | i := 0 129 | self.idx.Range(func(k interface{}, n interface{}) bool { 130 | i++ 131 | prid := k.(protocol.ID) 132 | self.mu.Lock() 133 | e := self.entries[n.(int)] 134 | if e.expires.Before(time.Now()) { 135 | self.del(prid) 136 | if self.sinkFunc != nil { 137 | self.sinkFunc(e.Result) 138 | } 139 | } 140 | self.mu.Unlock() 141 | return true 142 | }) 143 | } 144 | -------------------------------------------------------------------------------- /p2p/protocol-complex/service/service_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "crypto/sha1" 7 | "math/rand" 8 | "testing" 9 | "time" 10 | 11 | "github.com/ethereum/go-ethereum/log" 12 | "github.com/ethereum/go-ethereum/p2p" 13 | "github.com/ethereum/go-ethereum/p2p/discover" 14 | "github.com/ethereum/go-ethereum/p2p/protocols" 15 | 16 | "../protocol" 17 | ) 18 | 19 | func init() { 20 | log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StderrHandler)) 21 | } 22 | 23 | type testPeer struct { 24 | *protocols.Peer 25 | rw p2p.MsgReader 26 | } 27 | 28 | func newPeer(s *protocols.Spec) *testPeer { 29 | var nid [discover.NodeIDBits / 8]byte 30 | lrw, rwr := p2p.MsgPipe() 31 | p := protocols.NewPeer( 32 | p2p.NewPeer(discover.NodeID(nid), "testpeer", []p2p.Cap{}), 33 | lrw, 34 | s, 35 | ) 36 | return &testPeer{ 37 | Peer: p, 38 | rw: rwr, 39 | } 40 | } 41 | 42 | func TestRequestHandler(t *testing.T) { 43 | 44 | // make service and peer 45 | s := NewDemoService(8, 3, time.Millisecond*500) 46 | p := newPeer(protocol.Spec) 47 | 48 | // generate data for work 49 | data := make([]byte, 32) 50 | _, err := rand.Read(data) 51 | if err != nil { 52 | t.Fatal(err.Error()) 53 | } 54 | 55 | // inject easy request, should complete well within a second 56 | s.requestHandler(&protocol.Request{ 57 | Data: data, 58 | Difficulty: 2, 59 | }, p.Peer) 60 | 61 | // get the response 62 | rlpmsg, _ := p.rw.ReadMsg() 63 | resultmsg := &protocol.Result{} 64 | if err := rlpmsg.Decode(resultmsg); err != nil { 65 | t.Fatal(err.Error()) 66 | } 67 | 68 | // inject too high difficulty 69 | s.requestHandler(&protocol.Request{ 70 | Data: data, 71 | Difficulty: 9, 72 | }, p.Peer) 73 | 74 | // get the response 75 | rlpmsg, _ = p.rw.ReadMsg() 76 | statusmsg := &protocol.Status{} 77 | if err := rlpmsg.Decode(statusmsg); err != nil { 78 | t.Fatal(err.Error()) 79 | } else if statusmsg.Code != protocol.StatusAreYouKidding { 80 | t.Fatalf("Expected StatusGaveup (%d), got %d", protocol.StatusAreYouKidding, statusmsg.Code) 81 | } 82 | 83 | // change the difficulty so we can time out 84 | s.maxDifficulty = 128 85 | 86 | // start three jobs (maxjobs) 87 | for i := 0; i < 4; i++ { 88 | go s.requestHandler(&protocol.Request{ 89 | Data: data, 90 | Difficulty: 128, 91 | }, p.Peer) 92 | } 93 | 94 | rlpmsg, _ = p.rw.ReadMsg() 95 | if err := rlpmsg.Decode(statusmsg); err != nil { 96 | t.Fatal(err.Error()) 97 | } else if statusmsg.Code != protocol.StatusBusy { 98 | t.Fatalf("Expected StatusBusy (%d), got %d", protocol.StatusBusy, statusmsg.Code) 99 | } 100 | 101 | rlpmsg, _ = p.rw.ReadMsg() 102 | if err := rlpmsg.Decode(statusmsg); err != nil { 103 | t.Fatal(err.Error()) 104 | } else if statusmsg.Code != protocol.StatusGaveup { 105 | t.Fatalf("Expected StatusGaveup (%d), got %d", protocol.StatusGaveup, statusmsg.Code) 106 | } 107 | 108 | _ = statusmsg 109 | 110 | } 111 | 112 | func TestJob(t *testing.T) { 113 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 114 | defer cancel() 115 | 116 | // prepare data arrays 117 | data := make([]byte, 128) 118 | c, err := rand.Read(data) 119 | if err != nil { 120 | t.Fatal(err.Error()) 121 | } else if c != len(data) { 122 | t.Fatal("short read") 123 | } 124 | 125 | // mine 126 | j, err := doJob(ctx, data, 8) 127 | if err != nil { 128 | t.Fatal(err) 129 | } 130 | 131 | // check 132 | if !bytes.Equal(j.Data, data) { 133 | t.Fatalf("data mismatch, expected %x, got %x", j.Data, data) 134 | } 135 | checkData := make([]byte, len(data)+8) 136 | copy(checkData, data) 137 | copy(checkData[len(data):], j.Nonce) 138 | h := sha1.New() 139 | h.Write(checkData) 140 | result := h.Sum(nil) 141 | if !bytes.Equal(result, j.Hash) { 142 | t.Fatalf("hash mismatch, expected %x, got %x (check data %x)", result, j.Hash, checkData) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /p2p/protocol-complex/service/submit.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "../protocol" 8 | ) 9 | 10 | const ( 11 | defaultSubmitsCapacity = 1000 12 | ) 13 | 14 | type submitStore struct { 15 | serial uint64 // last request id sent from this node 16 | 17 | // handle submits 18 | entries []*protocol.Request // a wrapping array cache of requests used to retrieve the request data on a result response 19 | cursor int // the current write position on the wrapping array cache 20 | idx map[protocol.ID]*protocol.Request // index to look up the request cache though a request id 21 | capacity int // size of request cache (wrap threshold) 22 | 23 | mu sync.RWMutex 24 | } 25 | 26 | func newSubmitStore() *submitStore { 27 | return &submitStore{ 28 | entries: make([]*protocol.Request, defaultSubmitsCapacity), 29 | idx: make(map[protocol.ID]*protocol.Request), 30 | capacity: defaultSubmitsCapacity, 31 | } 32 | } 33 | 34 | // add submits to entry cache 35 | func (self *submitStore) Put(req *protocol.Request, id protocol.ID) error { 36 | self.mu.Lock() 37 | defer self.mu.Unlock() 38 | if _, ok := self.idx[id]; ok { 39 | return fmt.Errorf("entry already exists") 40 | } 41 | 42 | self.cursor++ 43 | self.cursor %= self.capacity 44 | if self.entries[self.cursor] != nil { 45 | delete(self.idx, self.entries[self.cursor].Id) 46 | } 47 | self.entries[self.cursor] = req 48 | self.idx[id] = req 49 | return nil 50 | } 51 | 52 | func (self *submitStore) Have(id protocol.ID) bool { 53 | self.mu.RLock() 54 | defer self.mu.RUnlock() 55 | return self.have(id) 56 | } 57 | 58 | func (self *submitStore) have(id protocol.ID) bool { 59 | _, ok := self.idx[id] 60 | return ok 61 | } 62 | 63 | func (self *submitStore) GetData(id protocol.ID) []byte { 64 | self.mu.Lock() 65 | defer self.mu.Unlock() 66 | if self.have(id) { 67 | return self.idx[id].Data 68 | } 69 | return nil 70 | } 71 | 72 | func (self *submitStore) GetDifficulty(id protocol.ID) uint8 { 73 | self.mu.Lock() 74 | defer self.mu.Unlock() 75 | if self.have(id) { 76 | return self.idx[id].Difficulty 77 | } 78 | return 0 79 | } 80 | 81 | func (self *submitStore) IncSerial() uint64 { 82 | self.mu.Lock() 83 | defer self.mu.Unlock() 84 | self.serial++ 85 | return self.serial 86 | } 87 | 88 | func (self *submitStore) LastSerial() uint64 { 89 | self.mu.RLock() 90 | defer self.mu.RUnlock() 91 | return self.serial 92 | } 93 | -------------------------------------------------------------------------------- /p2p/protocol-complex/service/work.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "./minipow" 8 | ) 9 | 10 | var ( 11 | mu = sync.Mutex{} 12 | ) 13 | 14 | type job struct { 15 | Data []byte 16 | Hash []byte 17 | Nonce []byte 18 | } 19 | 20 | func doJob(ctx context.Context, rawData []byte, difficulty uint8) (*job, error) { 21 | resultC := make(chan []byte) 22 | quitC := make(chan struct{}) 23 | 24 | workData := make([]byte, len(rawData)+8) 25 | copy(workData, rawData) 26 | 27 | go minipow.Mine(workData, int(difficulty), resultC, quitC, nil) 28 | 29 | var r []byte 30 | select { 31 | case <-ctx.Done(): 32 | quitC <- struct{}{} 33 | return nil, ctx.Err() 34 | case r = <-resultC: 35 | } 36 | 37 | j := &job{ 38 | Data: rawData, 39 | Nonce: workData[len(workData)-8:], 40 | Hash: r, 41 | } 42 | return j, nil 43 | } 44 | 45 | func checkJob(hash []byte, data []byte, nonce []byte) bool { 46 | if hash == nil || data == nil || nonce == nil { 47 | return false 48 | } 49 | return minipow.Check(hash, data, nonce) 50 | } 51 | -------------------------------------------------------------------------------- /p2p/protocol-complex/sim.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | "time" 12 | 13 | "github.com/ethereum/go-ethereum/log" 14 | "github.com/ethereum/go-ethereum/node" 15 | "github.com/ethereum/go-ethereum/p2p/enode" 16 | "github.com/ethereum/go-ethereum/p2p/simulations" 17 | "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 18 | 19 | colorable "github.com/mattn/go-colorable" 20 | 21 | "./protocol" 22 | "./resource" 23 | "./service" 24 | ) 25 | 26 | const ( 27 | defaultMaxDifficulty = 24 28 | defaultMinDifficulty = 8 29 | defaultSubmitDelay = time.Millisecond * 100 30 | defaultDataSize = 32 31 | defaultMaxTime = time.Second * 10 32 | defaultSimDuration = time.Second * 5 33 | defaultMaxJobs = 100 34 | defaultResourceApiHost = "http://localhost:8500" 35 | ) 36 | 37 | var ( 38 | loglevel = flag.Bool("v", false, "loglevel") 39 | useResource = flag.Bool("r", false, "use resource sink") 40 | ensAddr = flag.String("e", "", "ens name to post resource update") 41 | maxDifficulty uint8 42 | minDifficulty uint8 43 | maxTime time.Duration 44 | maxJobs int 45 | ) 46 | 47 | func init() { 48 | flag.Parse() 49 | if *loglevel { 50 | log.PrintOrigins(true) 51 | log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) 52 | } 53 | 54 | maxDifficulty = defaultMaxDifficulty 55 | minDifficulty = defaultMinDifficulty 56 | maxTime = defaultMaxTime 57 | maxJobs = defaultMaxJobs 58 | 59 | adapters.RegisterServices(newServices()) 60 | } 61 | 62 | func main() { 63 | a := adapters.NewSimAdapter(newServices()) 64 | 65 | n := simulations.NewNetwork(a, &simulations.NetworkConfig{ 66 | ID: "protocol-demo", 67 | DefaultService: "demo", 68 | }) 69 | defer n.Shutdown() 70 | 71 | var nids []enode.ID 72 | for i := 0; i < 5; i++ { 73 | c := adapters.RandomNodeConfig() 74 | nod, err := n.NewNodeWithConfig(c) 75 | if err != nil { 76 | log.Error(err.Error()) 77 | return 78 | } 79 | nids = append(nids, nod.ID()) 80 | } 81 | 82 | // TODO: need better assertion for network readiness 83 | n.StartAll() 84 | for i, nid := range nids { 85 | if i == 0 { 86 | continue 87 | } 88 | n.Connect(nids[0], nid) 89 | } 90 | 91 | go http.ListenAndServe(":8888", simulations.NewServer(n)) 92 | 93 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 94 | defer cancel() 95 | 96 | quitC := make(chan struct{}) 97 | trigger := make(chan enode.ID) 98 | events := make(chan *simulations.Event) 99 | sub := n.Events().Subscribe(events) 100 | // event sink on quit 101 | defer func() { 102 | sub.Unsubscribe() 103 | close(quitC) 104 | select { 105 | case <-events: 106 | default: 107 | } 108 | return 109 | }() 110 | 111 | action := func(ctx context.Context) error { 112 | for i, nid := range nids { 113 | if i == 0 { 114 | log.Info("appointed worker node", "node", nid.String()) 115 | go func(nid enode.ID) { 116 | trigger <- nid 117 | }(nid) 118 | continue 119 | } 120 | client, err := n.GetNode(nid).Client() 121 | if err != nil { 122 | return err 123 | } 124 | err = client.Call(nil, "demo_setDifficulty", 0) 125 | if err != nil { 126 | return err 127 | } 128 | 129 | go func(nid enode.ID) { 130 | timer := time.NewTimer(defaultSimDuration) 131 | for { 132 | select { 133 | case <-events: 134 | continue 135 | case <-quitC: 136 | return 137 | case <-ctx.Done(): 138 | return 139 | case <-timer.C: 140 | } 141 | log.Debug("stop sending", "node", nid) 142 | trigger <- nid 143 | return 144 | } 145 | }(nid) 146 | } 147 | return nil 148 | } 149 | check := func(ctx context.Context, nid enode.ID) (bool, error) { 150 | select { 151 | case <-ctx.Done(): 152 | default: 153 | } 154 | log.Warn("ok", "nid", nid) 155 | return true, nil 156 | } 157 | 158 | ctx, cancel = context.WithTimeout(context.Background(), time.Second*10) 159 | defer cancel() 160 | sim := simulations.NewSimulation(n) 161 | step := sim.Run(ctx, &simulations.Step{ 162 | Action: action, 163 | Trigger: trigger, 164 | Expect: &simulations.Expectation{ 165 | Nodes: nids, 166 | Check: check, 167 | }, 168 | }) 169 | if step.Error != nil { 170 | log.Error(step.Error.Error()) 171 | } 172 | for i, nid := range nids { 173 | if i == 0 { 174 | continue 175 | } 176 | log.Debug("stopping node", "nid", nid) 177 | n.Stop(nid) 178 | 179 | } 180 | sigC := make(chan os.Signal) 181 | signal.Notify(sigC, syscall.SIGINT) 182 | 183 | <-sigC 184 | 185 | return 186 | } 187 | 188 | func newServices() adapters.Services { 189 | haveWorker := false 190 | return adapters.Services{ 191 | "demo": func(node *adapters.ServiceContext) (node.Service, error) { 192 | var resourceEnsName string 193 | if *ensAddr != "" { 194 | resourceEnsName = *ensAddr 195 | } else { 196 | resourceEnsName = fmt.Sprintf("%x.mutable.test", node.Config.ID[:]) 197 | } 198 | resourceapi := resource.NewClient(defaultResourceApiHost, resourceEnsName) 199 | var sinkFunc service.ResultSinkFunc 200 | if *useResource { 201 | sinkFunc = resourceapi.ResourceSinkFunc() 202 | } 203 | params := service.NewDemoParams(sinkFunc, saveFunc) 204 | params.MaxJobs = maxJobs 205 | params.MaxTimePerJob = maxTime 206 | if !haveWorker { 207 | params.MaxDifficulty = maxDifficulty 208 | haveWorker = true 209 | } 210 | params.SubmitDelay = defaultSubmitDelay 211 | params.SubmitDataSize = defaultDataSize 212 | params.MaxSubmitDifficulty = defaultMaxDifficulty 213 | params.MinSubmitDifficulty = defaultMinDifficulty 214 | 215 | params.Id = node.Config.ID[:] 216 | return service.NewDemo(params) 217 | }, 218 | } 219 | } 220 | 221 | func saveFunc(nid []byte, id protocol.ID, difficulty uint8, data []byte, nonce []byte, hash []byte) { 222 | fmt.Fprintf(os.Stdout, "RESULT >> %x/%x : %x@%d|%x => %x\n", nid[:8], id, data, difficulty, nonce, hash) 223 | } 224 | -------------------------------------------------------------------------------- /solidity/escrow/escrow.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.20; 2 | 3 | contract escrow { 4 | 5 | uint256 public seq; 6 | 7 | // statuses: 8 | // 0 = not active 9 | // 1 = active 10 | // 2 = paid 11 | // 3 = released 12 | // 4 = withdrawn 13 | // 5 = cancelled 14 | struct participant { 15 | uint80 amount; 16 | uint8 status; 17 | address beneficiary; 18 | } 19 | 20 | struct escrowItem { 21 | bytes8 name; 22 | address owner; 23 | mapping(address => participant) participants; 24 | address[] participantsIdx; 25 | } 26 | 27 | participant[] participantsRegistry; 28 | escrowItem[] escrowItems; 29 | 30 | event Created(uint256 seq, bytes8 _name); 31 | event ParticipantPaid(uint256 seq, address participant); 32 | event ParticipantReleased(uint256 seq, address participant); 33 | event ParticipantCancelled(uint256 seq, address participant); 34 | event Finished(uint256 seq); 35 | 36 | 37 | function createEscrow(bytes8 _name) public { 38 | escrowItem memory e; 39 | seq++; 40 | 41 | e.owner = msg.sender; 42 | e.name = _name; 43 | escrowItems.push(e); 44 | emit Created(seq, _name); 45 | } 46 | 47 | function addParticipant(uint256 _seq, uint80 _amount, address _who, address _beneficiary) public { 48 | require(msg.sender == escrowItems[_seq-1].owner); 49 | require(escrowItems[_seq-1].participants[_who].status == 0); 50 | 51 | participant memory p; 52 | 53 | p.amount = _amount; 54 | p.beneficiary = _beneficiary; 55 | p.status = 1; 56 | 57 | escrowItems[_seq-1].participants[_who] = p; 58 | escrowItems[_seq-1].participantsIdx.push(_who); 59 | } 60 | 61 | // add an alias function without the tip steps 62 | function participantPut(uint256 _id) public payable { 63 | require(escrowItems[_id].participants[msg.sender].beneficiary != address(0x0)); 64 | require(escrowItems[_id].participants[msg.sender].amount <= msg.value); 65 | 66 | escrowItems[_id].participants[msg.sender].amount = uint80(msg.value); 67 | escrowItems[_id].participants[msg.sender].status = 2; 68 | emit ParticipantPaid(_id, msg.sender); 69 | } 70 | 71 | function participantRelease(uint256 _id) public { 72 | require(escrowItems[_id].participants[msg.sender].status == 2); 73 | 74 | escrowItems[_id].participants[msg.sender].status = 3; 75 | emit ParticipantReleased(_id, msg.sender); 76 | } 77 | 78 | // can only withdraw if all have released or if cancelled 79 | function participantPayout(uint256 _id) public returns (bool) { 80 | require(escrowItems[_id].participants[msg.sender].status > 2); 81 | 82 | // mode is 3 for release and 5 for cancelled 83 | // for payout to succeed, all must be released or own must be cancelled 84 | uint8 mode = 3; 85 | for (uint8 i = 0; i < escrowItems[_id].participantsIdx.length; i++) { 86 | if (escrowItems[_id].participants[escrowItems[_id].participantsIdx[i]].status == 5) { 87 | mode = 5; 88 | break; 89 | } 90 | require(escrowItems[_id].participants[escrowItems[_id].participantsIdx[i]].status == 3); 91 | } 92 | 93 | uint256 amountToSend = escrowItems[_id].participants[msg.sender].amount; 94 | 95 | // if one is cancelled but not self, require cancel first 96 | // else if cancelled, return to self 97 | // else send to beneficiary 98 | address beneficiary; 99 | 100 | if (escrowItems[_id].participants[msg.sender].status == 5) { 101 | beneficiary = msg.sender; 102 | } else if (mode == 5) { 103 | revert(); 104 | } else { 105 | beneficiary = escrowItems[_id].participants[msg.sender].beneficiary; 106 | } 107 | escrowItems[_id].participants[msg.sender].status = 4; 108 | escrowItems[_id].participants[msg.sender].amount = 0; 109 | if (!msg.sender.send(escrowItems[_id].participants[msg.sender].amount)) { 110 | escrowItems[_id].participants[msg.sender].amount = uint80(amountToSend); 111 | escrowItems[_id].participants[msg.sender].status = mode; 112 | return false; 113 | } 114 | return true; 115 | } 116 | 117 | // can cancel at any time except: 118 | // if payout has been made 119 | // if all participants including sender have released 120 | function participantCancel(uint256 _id) public { 121 | require(escrowItems[_id].participants[msg.sender].status < 4); 122 | 123 | uint8 releases; 124 | for (uint8 i = 0; i < escrowItems[_id].participantsIdx.length; i++) { 125 | if (escrowItems[_id].participants[escrowItems[_id].participantsIdx[i]].status == 3) { 126 | releases++; 127 | } 128 | } 129 | require(releases != escrowItems[_id].participantsIdx.length); 130 | escrowItems[_id].participants[msg.sender].status = 5; 131 | emit ParticipantCancelled(_id, msg.sender); 132 | } 133 | } 134 | 135 | -------------------------------------------------------------------------------- /solidity/escrow/escrow_test.go: -------------------------------------------------------------------------------- 1 | package escrow 2 | 3 | import ( 4 | "context" 5 | "crypto/ecdsa" 6 | "flag" 7 | "fmt" 8 | "math/big" 9 | "os" 10 | "testing" 11 | "time" 12 | 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 14 | "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" 15 | "github.com/ethereum/go-ethereum/common" 16 | "github.com/ethereum/go-ethereum/core" 17 | "github.com/ethereum/go-ethereum/crypto" 18 | "github.com/ethereum/go-ethereum/log" 19 | ) 20 | 21 | var ( 22 | ownerPrivKey *ecdsa.PrivateKey 23 | ownerAddress common.Address 24 | transactors []*bind.TransactOpts 25 | privKeys []*ecdsa.PrivateKey 26 | addresses []common.Address 27 | debugflag = flag.Bool("vv", false, "verbose output") 28 | ) 29 | 30 | func init() { 31 | var err error 32 | flag.Parse() 33 | loglevel := log.LvlInfo 34 | if *debugflag { 35 | loglevel = log.LvlTrace 36 | } 37 | hs := log.StreamHandler(os.Stderr, log.TerminalFormat(true)) 38 | hf := log.LvlFilterHandler(loglevel, hs) 39 | h := log.CallerFileHandler(hf) 40 | log.Root().SetHandler(h) 41 | 42 | for i := 0; i < 4; i++ { 43 | key, err := crypto.GenerateKey() 44 | if err != nil { 45 | panic(fmt.Sprintf("keygen fail: %v", err)) 46 | } 47 | privKeys = append(privKeys, key) 48 | addresses = append(addresses, crypto.PubkeyToAddress(key.PublicKey)) 49 | transactors = append(transactors, bind.NewKeyedTransactor(key)) 50 | log.Info("Generate key", "#", i, "privKey", fmt.Sprintf("%x", crypto.FromECDSA(privKeys[i])), "address", fmt.Sprintf("%x", addresses[i])) 51 | } 52 | 53 | ownerPrivKey, err = crypto.GenerateKey() 54 | if err != nil { 55 | panic(fmt.Sprintf("keygen fail: %v", err)) 56 | } 57 | ownerAddress = crypto.PubkeyToAddress(ownerPrivKey.PublicKey) 58 | 59 | } 60 | 61 | func newTestBackend() *backends.SimulatedBackend { 62 | return backends.NewSimulatedBackend(core.GenesisAlloc{ 63 | addresses[0]: {Balance: big.NewInt(10000000000)}, 64 | addresses[1]: {Balance: big.NewInt(10000000000)}, 65 | addresses[2]: {Balance: big.NewInt(10000000000)}, 66 | addresses[3]: {Balance: big.NewInt(10000000000)}, 67 | ownerAddress: {Balance: big.NewInt(10000000000)}, 68 | }) 69 | } 70 | 71 | func TestEscrow(t *testing.T) { 72 | backend := newTestBackend() 73 | deployTransactor := bind.NewKeyedTransactor(ownerPrivKey) 74 | deployTransactor.Value = big.NewInt(0) 75 | 76 | // deploy escrow contract 77 | escrow_addr, escrow_trans, escrow, err := DeployEscrow(deployTransactor, backend) 78 | if err != nil { 79 | t.Fatal(err) 80 | } 81 | backend.Commit() 82 | 83 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 84 | defer cancel() 85 | createC := make(chan *EscrowCreated) 86 | getC := make(chan interface{}) 87 | createFilterOpts := &bind.WatchOpts{ 88 | Start: nil, 89 | Context: ctx, 90 | } 91 | 92 | escrow.EscrowFilterer.WatchCreated(createFilterOpts, createC) 93 | go func() { 94 | select { 95 | case ev := <-createC: 96 | getC <- ev 97 | case <-ctx.Done(): 98 | t.Fatal(ctx.Err()) 99 | } 100 | }() 101 | 102 | // create a new escrow 103 | v, err := escrow.CreateEscrow(transactors[0], [8]byte{0x66, 0x6f, 0x64}) 104 | if err != nil { 105 | t.Fatal(err) 106 | } 107 | backend.Commit() 108 | log.Info("createescrow return", "v", v) 109 | ev := <-getC 110 | log.Warn("event", "ev", ev) 111 | 112 | // owner add a participant 113 | v, err = escrow.AddParticipant(transactors[0], ev.(*EscrowCreated).Seq, big.NewInt(4200), addresses[1], addresses[0]) 114 | if err != nil { 115 | t.Fatal(err) 116 | } 117 | backend.Commit() 118 | 119 | // owner add previously added participant -> FAIL 120 | v, err = escrow.AddParticipant(transactors[0], ev.(*EscrowCreated).Seq, big.NewInt(4200), addresses[1], addresses[0]) 121 | if err == nil { 122 | t.Fatalf("should have failed: %v", v) 123 | } 124 | backend.Commit() 125 | 126 | // non-owner add a partiticant -> FAIL 127 | v, err = escrow.AddParticipant(transactors[1], ev.(*EscrowCreated).Seq, big.NewInt(4200), addresses[1], addresses[0]) 128 | if err == nil { 129 | t.Fatalf("should have failed: %v", v) 130 | } 131 | backend.Commit() 132 | 133 | _ = escrow_addr 134 | _ = escrow_trans 135 | } 136 | -------------------------------------------------------------------------------- /solidity/upgrade/AbstractMain.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | import './Store.sol'; 4 | import './lib/owned.sol'; 5 | 6 | contract AbstractMain is owned { 7 | Store public store; 8 | 9 | /** 10 | * set the data store for the contract 11 | * can only be called if a store doesn't yet exist 12 | * that way we can't cheat and change the store after upgrade 13 | * @param _store address of persistent store contract 14 | */ 15 | function setStore(address _store) { 16 | address empty; 17 | require(store == empty); 18 | store = Store(_store); 19 | } 20 | 21 | /** 22 | * deletes contract, and transfers funds to new contract 23 | * only owner can do this 24 | * @param _beneficiary address of contract to receive funds 25 | */ 26 | function kill(address _beneficiary) { 27 | require(isOwned()); 28 | selfdestruct(_beneficiary); 29 | } 30 | 31 | /** 32 | * transfers ownership of contract to new owner 33 | * @param _owner address of new owner 34 | */ 35 | function transfer(address _owner) onlyowner { 36 | owner = _owner; 37 | } 38 | 39 | /** 40 | * returns true if owner of contract is current called 41 | */ 42 | function isOwned() constant returns(bool) { 43 | return owner == msg.sender; 44 | } 45 | 46 | /** 47 | * fallback for accepting payments 48 | */ 49 | function () payable {} 50 | } 51 | -------------------------------------------------------------------------------- /solidity/upgrade/Main.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | contract Main is AbstractMain { 4 | uint8 public xyzzy; 5 | function Main() { 6 | xyzzy = 42; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /solidity/upgrade/MainUpgrade.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | contract MainUpgrade is AbstractMain { 4 | uint16 public plugh; 5 | function MainUpgrade() { 6 | plugh = 666; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /solidity/upgrade/README.md: -------------------------------------------------------------------------------- 1 | # Upgradeable contract 2 | 3 | An attempt at a simple example of how to implement a contract where the bytecode of the main interface can be changed while retaining persistent storage and eth balance. 4 | 5 | The `Upgrader.create()` will only create an instance of the abstract contract. The intention is that `Upgrader.upgrade(address)` be called immediately after creation, passing the first proper implementation of the contract. 6 | 7 | ## Running in remix 8 | 9 | Run `sh remix.sh > remixcodefile.txt` and paste the contents of `remixcodefile.txt` into remix. 10 | 11 | First we instantiate the upgrader and create the first contract: 12 | 13 | 1. Set value > 0 14 | 2. Upgrader -> Create 15 | 3. Upgrader -> create 16 | 4. Upgrader -> current: != 0x0 17 | 5. Upgrader -> check: bool == true, uint256 > 0 18 | 19 | Attempt upgrade, first without and with transfer of ownership of Main contract: 20 | 21 | 1. Main -> Create 22 | 2. copy address of Main 23 | 3. Upgrader -> upgrade("address of Main"): throws 24 | 4. copy address of Upgrader 25 | 5. Main -> transfer("address of Upgrader") 26 | 6. Upgrader -> upgrade("address of Main"): triggers event Upgrade with address of Main 27 | 7. Upgrader -> current(): == address of Main 28 | 8. Upgrader -> check: bool == true, uint256 > 0 29 | 30 | Attempt to alter store after upgrade 31 | 32 | 1. Store -> Create 33 | 2. copy address of Store 34 | 3. Main -> setStore("address of Store"): throws 35 | 36 | Attempt another upgrade, with preset store in Main contract 37 | 38 | 1. Store -> Create 39 | 2. copy new address of Store 40 | 3. MainUpgrade -> Create 41 | 4. MainUpgrade -> setStore("address of Store") 42 | 5. copy address of MainUpgrade 43 | 6. Upgrader -> upgrade("address of MainUpgrade"): throws 44 | 7. Upgrader -> current(): == address of Main (not MainUpgrade) 45 | 46 | Attempt to steal eth by killing contract explicitly, transferring ownership 47 | 48 | 1. copy address of MainUpgrade 49 | 2. Main -> kill("address of MainUpgrade"): throws because Main is owned by Upgrader 50 | 3. Main -> transfer("address of MainUpgrade"): silently doesn't execute 51 | 4. Main -> kill("address of MainUpgrade"): throws because Main is owned by Upgrader 52 | 5. Main -> check(): bool == true, uint256 > 0 53 | -------------------------------------------------------------------------------- /solidity/upgrade/Store.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | contract Store { 4 | bool public foo; 5 | 6 | /** 7 | * for proof of persistent store 8 | * set this explicitly when the first contract is created 9 | */ 10 | function poke() { 11 | foo = true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /solidity/upgrade/Upgrader.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | import './Store.sol'; 4 | import './AbstractMain.sol'; 5 | 6 | contract Upgrader { 7 | /** 8 | * holds the address of the latest version of Main 9 | * servers as authenticator for those wishing to use the Main contract 10 | */ 11 | address public current; 12 | 13 | event Upgraded(address newaddress); 14 | 15 | /** 16 | * enable contract to receive funds on creation 17 | */ 18 | function Upgrader() payable { 19 | } 20 | 21 | /** 22 | * creates new main and store instances 23 | * assigns new store to main contract 24 | * transfers eth from upgrader contract to main 25 | */ 26 | function create() { 27 | Store store = new Store(); 28 | store.poke(); // store foo set to true 29 | AbstractMain main = new AbstractMain(); 30 | main.setStore(store); 31 | main.transfer(this.balance); 32 | current = main; 33 | } 34 | 35 | /** 36 | * accepts a main instance, and assigns the existing store of the current Main to it 37 | * transfers eth from the current Main to the new Main contract 38 | * the Upgrader contract MUST be the owner of the new Main contract (see AbstractMain.transfer(address)) 39 | * @param _main address of new Main instance 40 | */ 41 | function upgrade(address _main) { 42 | AbstractMain newmain = AbstractMain(_main); 43 | require(newmain.isOwned()); // will throw if AbstractMain.transfer with our address hasn't been called 44 | AbstractMain oldmain = AbstractMain(current); 45 | Store store = oldmain.store(); 46 | newmain.setStore(store); // will throw if store is already set 47 | oldmain.kill(newmain); 48 | current = newmain; // we know we own the new contract, and moneys have been passed 49 | Upgraded(newmain); 50 | } 51 | 52 | /** 53 | * for verifying that the Store instance has persisted 54 | * and for verifying that eth has been transferred to new Main after upgrade/creation 55 | */ 56 | function check() constant returns(bool, uint256) { 57 | AbstractMain main = AbstractMain(current); 58 | return (main.store().foo(), main.balance); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /solidity/upgrade/lib/owned.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | contract owned { 4 | address owner; 5 | 6 | modifier onlyowner() { 7 | if (msg.sender == owner) { 8 | _; 9 | } 10 | } 11 | 12 | function owned() { 13 | owner = msg.sender; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solidity/upgrade/remix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function f_strip { 4 | for l in $(cat $1 | grep -ve ^pragma | grep -ve ^import); do 5 | echo $l 6 | done 7 | } 8 | 9 | IFS=$'\n' 10 | echo "pragma solidity ^0.4.0;" 11 | echo 12 | for i in lib/*.*; do 13 | >&2 echo "adding $i" 14 | f_strip $i 15 | done 16 | src=( Store.sol AbstractMain.sol Upgrader.sol Main.sol MainUpgrade.sol ) 17 | for i in $(seq 0 $((${#src[@]}-1))); do 18 | f=${src[$i]} 19 | >&2 echo "adding $f" 20 | f_strip $f 21 | done 22 | 23 | -------------------------------------------------------------------------------- /swarm/bash/bzz.inc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # defaults 4 | remoteurl="http://swarm-gateways.net" 5 | url="http://127.0.0.1:8500" 6 | 7 | # flag store vars 8 | o_raw=0 9 | o_remote=0 10 | 11 | while test $# -gt 0 12 | do 13 | case $1 in 14 | -h|--help) 15 | echo -e $helpout 16 | echo 17 | echo "-h,--help display this help" 18 | echo "-x raw output" 19 | echo "-r use fallback gateway" 20 | exit 0 21 | ;; 22 | -x) 23 | shift 24 | o_raw=1 25 | ;; 26 | -r) 27 | shift 28 | o_remote=1 29 | ;; 30 | -*) 31 | shift 32 | ;; 33 | *) 34 | break 35 | ;; 36 | esac 37 | done 38 | 39 | f=$1 40 | 41 | # remote flag sets gateways as api 42 | if [ $o_remote -gt 0 ] 43 | then 44 | url=$remoteurl 45 | elif [ ! -z $BZZAPI ] 46 | then 47 | url=$BZZAPI 48 | fi 49 | 50 | -------------------------------------------------------------------------------- /swarm/bash/bzz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dir="${BASH_SOURCE%/*}" 4 | if [ ! -d $dir ] 5 | then 6 | dir=$PWD 7 | else 8 | dir=`realpath $PWD` 9 | fi 10 | 11 | helpout=$(cat <&2 echo "unable to create temporary file" && exit 1 32 | cat - > $f 33 | fi 34 | 35 | # check if file has content 36 | [ ! -s $f ] && >&2 echo "cannot post empty file $f" && exit 1 37 | 38 | # get type of file 39 | bzzmime=`file --mime-type -b $f` 40 | 41 | # build the swarm command and result url parts 42 | bzzcmd="swarm --bzzapi $url --mime $bzzmime" 43 | prefix=$url 44 | postfix="" 45 | 46 | # omit manifest creation on raw 47 | if [ $o_raw -gt 0 ] 48 | then 49 | bzzcmd="$bzzcmd --manifest=false" 50 | prefix="${prefix}/bzz-raw:/" 51 | postfix="/?content_type=$bzzmime" 52 | else 53 | prefix="${prefix}/bzz:/" 54 | fi 55 | 56 | $bzzcmd up $f | while read -r a 57 | do 58 | if [[ $a =~ ^([a-zA-Z0-9]+)$ ]]; 59 | then 60 | bzzhash=${BASH_REMATCH[1]} 61 | echo "$prefix$bzzhash/$postfix" 62 | fi 63 | # bzzhash=${BASH_REMATCH[1]} 64 | # if [[ $a =~ (\"hash\": \"([a-zA-Z0-9]+)\") ]]; 65 | # then 66 | # bzzhash=${BASH_REMATCH[2]} 67 | # echo "$prefix$bzzhash$postfix/" 68 | # elif [[ $a =~ ^([a-zA-Z0-9]+)$ ]]; 69 | # then 70 | # bzzhash=${BASH_REMATCH[1]} 71 | # echo "$prefix$bzzhash/$postfix" 72 | # fi 73 | done 74 | -------------------------------------------------------------------------------- /swarm/bash/bzzseed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # preamble 4 | 5 | # find include location 6 | dir="${BASH_SOURCE%/*}" 7 | if [ ! -d $dir ] 8 | then 9 | dir=$PWD 10 | else 11 | dir=`realpath $PWD` 12 | fi 13 | 14 | # help output head 15 | helpout=$(cat < $BZZPATH/$BZZDIR.log 91 | 92 | -------------------------------------------------------------------------------- /swarm/docker/jaeger-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | IMG="swarm:hs" 4 | FILESIZEK="50000" 5 | 6 | case $1 in 7 | "start") 8 | docker network create --internal --subnet 10.1.2.0/24 swarminternal 9 | docker run -d --ip 10.1.2.2 --network swarminternal --rm --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 16686:16686 jaegertracing/all-in-one:1.8 10 | docker run -d --ip 10.1.2.11 --network swarminternal --rm -e PATH=/:/bin:/usr/bin:/usr/local/bin -e PASSWORD=tralala --name bzz1 -h bz1 $IMG --tracing --tracing.endpoint 10.1.2.2:6831 --bzzport 80 --httpaddr 0.0.0.0 --tracing.svc "bz1" 11 | # wait for nodes to start 12 | sleep 5 13 | ENODE=`docker exec bzz1 /geth attach /root/.ethereum/bzzd.ipc --exec admin.nodeInfo.enode` 14 | ENODENEW=`echo -n $ENODE | sed -e "s/^\"\(.*\)@127.0.0.1:\([0-9]*\)?.*$/\1@10.1.2.11:\2/g"` 15 | echo "b$i: '$ENODE'" 16 | echo "a$i: '$ENODENEW'" 17 | for i in {2..4}; do 18 | docker run -d --ip 10.1.2.1$i --network swarminternal --rm -e PATH=/:/bin:/usr/bin:/usr/local/bin -e PASSWORD=tralala --name bzz$i -h bz$i $IMG --tracing --tracing.endpoint 10.1.2.2:6831 --bzzport 80 --httpaddr 0.0.0.0 --tracing.svc "bz$i" --bootnodes $ENODENEW 19 | done 20 | ;; 21 | "smoke") 22 | docker exec bzz1 /swarm-smoke --verbosity 5 --single --hosts 10.1.2.11,10.1.2.14 --filesize $FILESIZEK upload_and_sync 23 | ;; 24 | "status") 25 | for i in {1..4}; do 26 | docker exec bzz$i /geth attach /root/.ethereum/bzzd.ipc --exec admin.peers 27 | done 28 | ;; 29 | "stop") 30 | for i in {4..1}; do 31 | docker stop bzz$i 32 | done 33 | docker stop jaeger 34 | docker network rm swarminternal 35 | ;; 36 | esac 37 | -------------------------------------------------------------------------------- /swarm/docker/pss-ssl-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | IMG="swarm:pzz" 4 | #IMG="lash/swarm:pss" 5 | 6 | case $1 in 7 | "start") 8 | docker network create --internal --subnet 10.1.3.0/24 pssnet 9 | docker run -d --ip 10.1.3.11 --network pssnet --rm -e PATH=/:/bin:/usr/bin:/usr/local/bin -e PASSWORD=tralala --name pss1 -h ps1 $IMG 10 | # wait for nodes to start 11 | sleep 5 12 | ENODE=`docker exec pss1 /geth attach /root/.ethereum/bzzd.ipc --exec admin.nodeInfo.enode` 13 | ENODENEW=`echo -n $ENODE | sed -e "s/^\"\(.*\)@127.0.0.1:\([0-9]*\)?.*$/\1@10.1.3.11:\2/g"` 14 | echo "b$i: '$ENODE'" 15 | echo "a$i: '$ENODENEW'" 16 | for i in {2..4}; do 17 | docker run -d --ip 10.1.3.1$i --network pssnet --rm -e PATH=/:/bin:/usr/bin:/usr/local/bin -e PASSWORD=tralala --name pss$i -h ps$i $IMG --bootnodes $ENODENEW 18 | done 19 | ;; 20 | "keys") 21 | for i in {1..4}; do 22 | docker exec pss$i cat /ecpriv.txt 23 | echo 24 | done 25 | ;; 26 | "status") 27 | for i in {1..4}; do 28 | docker exec pss$i /geth attach /root/.ethereum/bzzd.ipc --exec admin.peers 29 | done 30 | ;; 31 | "stop") 32 | for i in {4..1}; do 33 | docker stop pss$i 34 | done 35 | docker network rm pssnet 36 | ;; 37 | esac 38 | -------------------------------------------------------------------------------- /swarm/docker/pss-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | IMG="lash/swarm:psss" 4 | #IMG="ethdevops/swarm:latest" 5 | WSFLAGS="--ws --wsapi=pss,bzz,admin --wsaddr=0.0.0.0 --wsorigins=*" 6 | BZZFLAGS="--httpaddr=0.0.0.0" 7 | 8 | case $1 in 9 | "start") 10 | docker network create --subnet 10.1.3.0/24 pssnet 11 | echo "docker run -d --ip 10.1.3.11 --network pssnet --rm -e PATH=/:/bin:/usr/bin:/usr/local/bin -e PASSWORD=tralala --name pss1 -h ps1 $IMG --nat=extip:10.1.3.11 $BZZFLAGS $WSFLAGS" 12 | docker run -d --ip 10.1.3.11 --network pssnet --rm -e PATH=/:/bin:/usr/bin:/usr/local/bin -e PASSWORD=tralala --name pss1 -h ps1 $IMG --nat=extip:10.1.3.11 $BZZFLAGS $WSFLAGS 13 | # wait for nodes to start 14 | sleep 5 15 | ENODE=`docker exec pss1 geth attach /root/.ethereum/bzzd.ipc --exec admin.nodeInfo.enode` 16 | #ENODENEW=`echo -n $ENODE | sed -e "s/^\"\(.*\)@127.0.0.1:\([0-9]*\).*$/\1@10.1.3.11:\2/g"` 17 | ENODENEW=`echo -n $ENODE | sed -e "s/\"//g"` 18 | echo "b$i: '$ENODE'" 19 | echo "a$i: '$ENODENEW'" 20 | for i in {2..4}; do 21 | docker run -d --ip 10.1.3.1$i --network pssnet --rm -e PATH=/:/bin:/usr/bin:/usr/local/bin -e PASSWORD=tralala --name pss$i -h ps$i $IMG --nat=extip:10.1.3.1$i --bootnodes=$ENODENEW $BZZFLAGS $WSFLAGS 22 | done 23 | ;; 24 | "status") 25 | for i in {1..4}; do 26 | docker exec pss$i geth attach /root/.ethereum/bzzd.ipc --exec admin.peers 27 | done 28 | ;; 29 | "stop") 30 | for i in {4..1}; do 31 | docker stop pss$i 32 | done 33 | docker network rm pssnet 34 | ;; 35 | esac 36 | -------------------------------------------------------------------------------- /swarm/mutable-resources/client-digest/mru.js: -------------------------------------------------------------------------------- 1 | var web3 = require("web3"); 2 | 3 | if (module !== undefined) { 4 | module.exports = { 5 | digest: feedUpdateDigest 6 | } 7 | } 8 | 9 | var topicLength = 32; 10 | var userLength = 20; 11 | var timeLength = 7; 12 | var levelLength = 1; 13 | var headerLength = 8; 14 | var updateMinLength = topicLength + userLength + timeLength + levelLength + headerLength; 15 | 16 | function feedUpdateDigest(request /*request*/, data /*UInt8Array*/) { 17 | var topicBytes = undefined; 18 | var userBytes = undefined; 19 | var protocolVersion = 0; 20 | 21 | protocolVersion = request.protocolVersion 22 | 23 | try { 24 | topicBytes = web3.utils.hexToBytes(request.feed.topic); 25 | } catch(err) { 26 | console.error("topicBytes: " + err); 27 | return undefined; 28 | } 29 | 30 | try { 31 | userBytes = web3.utils.hexToBytes(request.feed.user); 32 | } catch(err) { 33 | console.error("topicBytes: " + err); 34 | return undefined; 35 | } 36 | 37 | var buf = new ArrayBuffer(updateMinLength + data.length); 38 | var view = new DataView(buf); 39 | var cursor = 0; 40 | 41 | view.setUint8(cursor, protocolVersion) // first byte is protocol version. 42 | cursor+=headerLength; // leave the next 7 bytes (padding) set to zero 43 | 44 | topicBytes.forEach(function(v) { 45 | view.setUint8(cursor, v); 46 | cursor++; 47 | }); 48 | 49 | userBytes.forEach(function(v) { 50 | view.setUint8(cursor, v); 51 | cursor++; 52 | }); 53 | 54 | // time is little-endian 55 | view.setUint32(cursor, request.epoch.time, true); 56 | cursor += 7; 57 | 58 | view.setUint8(cursor, request.epoch.level); 59 | cursor++; 60 | 61 | data.forEach(function(v) { 62 | view.setUint8(cursor, v); 63 | cursor++; 64 | }); 65 | //console.log(web3.utils.bytesToHex(new Uint8Array(buf))) 66 | 67 | return web3.utils.sha3(web3.utils.bytesToHex(new Uint8Array(buf))); 68 | } 69 | 70 | request = {"feed":{"topic":"0x4b680e0ac418934e5468daea238ffc8e25941200ff35a99eee2b1c52357f8c2a","user":"0x1d66d3fa0250e6e3085ec4ee90a7eafb176ebfb8"},"epoch":{"time":1544905717,"level":25},"protocolVersion":0} 71 | data = new Uint8Array([0x66, 0x6f, 0x6f]) 72 | 73 | // data payload 74 | //data = new Uint8Array([5,154,15,165,62]) 75 | 76 | // request template, obtained calling http://localhost:8500/bzz-feed:/?user=<0xUSER>&topic=<0xTOPIC>&meta=1 77 | //request = {"feed":{"topic":"0x1234123412341234123412341234123412341234123412341234123412341234","user":"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"},"epoch":{"time":1538650124,"level":25},"protocolVersion":0} 78 | 79 | // obtain digest 80 | digest = feedUpdateDigest(request, data) 81 | 82 | console.log(digest) 83 | -------------------------------------------------------------------------------- /swarm/mutable-resources/client-digest/mrucli.js: -------------------------------------------------------------------------------- 1 | var web3 = require("web3"); 2 | var flags = require("flags"); 3 | var fs = require("fs"); 4 | var mru = require("./mru.js"); 5 | 6 | flags.defineString("topic", "", "topic (optional, 0x prefixed hex)"); 7 | flags.defineBoolean("hex", false, "data input is hex encoded"); 8 | flags.defineString("user", "", "user (0x prefixed hex)"); 9 | flags.defineInteger("time", 0, "epoch time"); 10 | flags.defineInteger("level", 25, "epoch level"); 11 | 12 | var restArgs = flags.parse(); 13 | var inBuf = undefined; 14 | 15 | if (restArgs.length == 0) { 16 | inBuf = fs.readFileSync(0); 17 | } else { 18 | inBuf = fs.readFileSync(restArgs[0]); 19 | } 20 | // 21 | //if (flags.get("hex")) { 22 | // inBuf = inBuf.toString("ascii"); 23 | //} else { 24 | //inBuf = web3.utils.bytesToHex(inBuf); 25 | inBuf = web3.utils.bytesToHex(inBuf); 26 | //} 27 | 28 | var mruData = { 29 | "feed":{ 30 | "topic": flags.get("topic"), 31 | "user": flags.get("user"), 32 | }, 33 | "epoch": { 34 | "time": flags.get("time"), 35 | "level": flags.get("level"), 36 | }, 37 | protocolVersion: 0 38 | }; 39 | 40 | mruResult = mru.digest(mruData, inBuf); 41 | console.log(mruResult); 42 | -------------------------------------------------------------------------------- /swarm/mutable-resources/client-digest/topic.js: -------------------------------------------------------------------------------- 1 | var web3 = require("web3"); 2 | 3 | zeros = "0000000000000000000000000000000000000000000000000000000000000000"; 4 | 5 | module.exports = { 6 | toTopic: toTopic, 7 | } 8 | 9 | function setTopicRight(data, topic) { 10 | if (!web3.utils.isHex(topic)) { 11 | throw "topic must be hex"; 12 | return ""; 13 | } 14 | for (i = 0; i < topic.length / 2; i++) { 15 | var nameByte = ""; 16 | if (i < name.length) { 17 | nameByte = name.charCodeAt(i); 18 | } 19 | var topicByte = parseInt(topic.substring(i*2, (i*2)+2), 16); 20 | topicXored += ("0"+(nameByte ^ topicByte).toString(16)).slice(-2); 21 | } 22 | } 23 | 24 | function setTopicLeft(name, topic=zeros) { 25 | // check topic sanity 26 | if (!web3.utils.isHex(topic)) { 27 | throw "topic must be hex"; 28 | return ""; 29 | } 30 | 31 | // check name sanity 32 | if (typeof name !== "string") { 33 | throw "name must be string"; 34 | return ""; 35 | } else if (name.length > 32) { 36 | throw "name must be maximum 32 bytes long"; 37 | return ""; 38 | } 39 | 40 | // topic should be a 32 byte padded hex string in the manifest 41 | var topicPadded = zeros + topic; 42 | topic = topicPadded.slice(-64); 43 | 44 | var topicXored = ""; 45 | var nameLimit; 46 | if (name.length > 32) { 47 | nameLimit = 32; 48 | } else { 49 | nameLimit = name.length; 50 | } 51 | for (i = 0; i < topic.length / 2; i++) { 52 | var nameByte = ""; 53 | if (i < name.length) { 54 | nameByte = name.charCodeAt(i); 55 | } 56 | var topicByte = parseInt(topic.substring(i*2, (i*2)+2), 16); 57 | topicXored += ("0"+(nameByte ^ topicByte).toString(16)).slice(-2); 58 | } 59 | 60 | return topicXored; 61 | } 62 | 63 | function getFeedManifestFromHex(topic, name, user) { 64 | 65 | // check user address param sanity 66 | if (!web3.utils.isHex(user)) { 67 | throw "user must be hex"; 68 | return ""; 69 | } 70 | if (user.substring(0, 2) != "0x") { 71 | user = "0x"+user; 72 | } 73 | if (user.length != 42) { 74 | throw "user must be 20 byte ethereum address"; 75 | return ""; 76 | } 77 | 78 | return '{"entries":[{"contentType":"application/bzz-feed","mod_time":"0001-01-01T00:00:00Z","feed":{"topic":"0x' + getFeedTopic(topic, name) + '","user":"' + user + '"}}]}'; 79 | 80 | } 81 | 82 | //console.log(getFeedManifestFromHex("0x660000000000000000000000000000000000000000000000000000000000002a", "foo", "19cb96e2fcf9afd95ef06a504ca4feb89c05ca88")); 83 | //console.log(getFeedManifestFromHex("0x2a", "foo", "19cb96e2fcf9afd95ef06a504ca4feb89c05ca88")); 84 | -------------------------------------------------------------------------------- /swarm/mutable-resources/server-digest/digest.go: -------------------------------------------------------------------------------- 1 | //TODO: add signing 2 | package main 3 | 4 | import ( 5 | "flag" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/common/hexutil" 11 | "github.com/ethereum/go-ethereum/log" 12 | "github.com/ethereum/go-ethereum/swarm/storage/mru" 13 | "github.com/ethereum/go-ethereum/swarm/storage/mru/lookup" 14 | ) 15 | 16 | var ( 17 | topicFlag *string = flag.String("topic", "", "topic (optional)") 18 | nameFlag *string = flag.String("name", "", "(optional, not implemented") 19 | levelFlag *int = flag.Int("level", 25, "epoch level (default 25)") 20 | timeFlag *int = flag.Int("time", 0, "time (default 0") 21 | userFlag *string = flag.String("user", "", "user address (in hex)") 22 | signFlag *bool = flag.Bool("s", false, "sign digest (optional, not implemented") 23 | hexFlag *bool = flag.Bool("h", false, "input is hex encoded") 24 | verboseFlag *bool = flag.Bool("v", false, "print debug output") 25 | 26 | user common.Address 27 | topic mru.Topic 28 | epochLevel uint8 29 | epochTime uint64 30 | inFile *os.File 31 | ) 32 | 33 | func croak(r int, s string) { 34 | fmt.Fprintln(os.Stderr, "Error: ", s) 35 | if r == 1 { 36 | fmt.Println("\nUsage: digest.go --user [options] [file]\n\tIf file is omitted, data will be read from stdin\n") 37 | flag.PrintDefaults() 38 | } 39 | os.Exit(r) 40 | } 41 | 42 | func init() { 43 | var err error 44 | flag.Parse() 45 | 46 | if *verboseFlag { 47 | log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(6, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))) 48 | } 49 | 50 | userBytes, err := hexutil.Decode(*userFlag) 51 | if err != nil { 52 | croak(1, fmt.Sprintf("Invalid user address '%s'", *userFlag)) 53 | } 54 | user = common.BytesToAddress(userBytes) 55 | if *topicFlag != "" { 56 | topicBytes, err := hexutil.Decode(*topicFlag) 57 | if err != nil { 58 | croak(1, fmt.Sprintf("Invalid topic '%s'", *topicFlag)) 59 | } else if len(topic) > mru.TopicLength { 60 | croak(1, fmt.Sprintf("Topic too long, max %d", mru.TopicLength)) 61 | } 62 | topic, err = mru.NewTopic("", topicBytes) 63 | if err != nil { 64 | croak(1, fmt.Sprintf("Topic encode fail: %v", err)) 65 | } 66 | } 67 | 68 | // without arg read from stdin 69 | if len(flag.Args()) == 0 { 70 | inFile = os.Stdin 71 | } else { 72 | inFile, err = os.Open(flag.Args()[0]) 73 | if err != nil { 74 | croak(2, fmt.Sprintf("Could not open file '%s': %v", flag.Args()[0], err)) 75 | } 76 | 77 | } 78 | epochLevel = uint8(*levelFlag) 79 | epochTime = uint64(*timeFlag) 80 | } 81 | 82 | func main() { 83 | r := mru.NewFirstRequest(topic) 84 | r.User = user 85 | // r.ResourceUpdate.UpdateLookup.Epoch = lookup.Epoch{ 86 | // Level: epochLevel, 87 | // Time: epochTime, 88 | // } 89 | r.Epoch = lookup.Epoch{ 90 | Level: epochLevel, 91 | Time: epochTime, 92 | } 93 | 94 | // TODO: input length check 95 | data := make([]byte, 8192) 96 | c, err := inFile.Read(data) 97 | if err != nil { 98 | croak(3, fmt.Sprintf("Data read fail: %v", err)) 99 | } 100 | 101 | if *hexFlag { 102 | realData, err := hexutil.Decode(string(data[:c])) 103 | if err != nil { 104 | croak(3, fmt.Sprintf("Data hex decode fail '%v': %v", string(data), err)) 105 | } 106 | data = realData 107 | c = len(realData) 108 | } 109 | r.SetData(data[:c]) 110 | 111 | digest, err := r.GetDigest() 112 | if err != nil { 113 | croak(4, fmt.Sprintf("Digest generation fail: %v", err)) 114 | } 115 | fmt.Println(hexutil.Encode(digest.Bytes())) 116 | } 117 | -------------------------------------------------------------------------------- /swarm/mutable-resources/server-digest/signmru.go: -------------------------------------------------------------------------------- 1 | // this file is obsolete 2 | // signature option should instead be addded to digest.go 3 | package main 4 | 5 | import ( 6 | "encoding/hex" 7 | "flag" 8 | "fmt" 9 | "io/ioutil" 10 | "os" 11 | "os/user" 12 | "strings" 13 | "syscall" 14 | 15 | "golang.org/x/crypto/ssh/terminal" 16 | 17 | "github.com/ethereum/go-ethereum/accounts/keystore" 18 | "github.com/ethereum/go-ethereum/common/hexutil" 19 | "github.com/ethereum/go-ethereum/crypto" 20 | "github.com/ethereum/go-ethereum/log" 21 | "github.com/ethereum/go-ethereum/swarm/storage/mru" 22 | ) 23 | 24 | var ( 25 | g_dir string 26 | g_arg string 27 | g_file string 28 | g_help bool 29 | g_passfile string 30 | g_pass string 31 | ) 32 | 33 | func main() { 34 | 35 | usr, err := user.Current() 36 | if err != nil { 37 | log.Error("Could not get user info to resolve homedir") 38 | os.Exit(1) 39 | } 40 | 41 | var debug bool 42 | defaultdatadir := fmt.Sprintf("%s/.ethereum", usr.HomeDir) 43 | flag.StringVar(&g_dir, "d", defaultdatadir, "datadir") 44 | flag.BoolVar(&g_help, "h", false, "show help") 45 | flag.BoolVar(&debug, "v", false, "show debug info") 46 | flag.StringVar(&g_passfile, "p", "", "passwordfile") 47 | 48 | flag.Usage = func() { 49 | fmt.Println(` 50 | ****************************** 51 | * WARNING! WARNING! WARNING! * 52 | ****************************** 53 | 54 | this program will output your private key in hex format to the terminal. Your private key should be kept secret, at the risk of losing all funds within it, and possibly any accounts derived from it. 55 | 56 | Some recommended precautions: 57 | * Make sure noone is looking over your shoulder when you run this program. 58 | * Never store your private key on a digital device with insufficient encryption 59 | * Only use this tool on an airgapped machine 60 | 61 | The author of this application assumes no warranty or liability. 62 | 63 | ---- 64 | Usage: ethexport [flags] ") 65 | 66 | If argument is account hex, the keystore subdir of the datadir will be searched for a matching account 67 | If argument is keyfile, the -d flag will be ignored 68 | `) 69 | flag.PrintDefaults() 70 | } 71 | flag.Parse() 72 | 73 | if g_help { 74 | flag.Usage() 75 | os.Exit(0) 76 | } 77 | 78 | lvl := log.LvlError 79 | if debug { 80 | lvl = log.LvlDebug 81 | } 82 | h := log.LvlFilterHandler(lvl, log.StderrHandler) 83 | log.Root().SetHandler(h) 84 | 85 | if g_dir == "" { 86 | g_dir = defaultdatadir 87 | } 88 | 89 | if g_passfile != "" { 90 | pass, err := ioutil.ReadFile(g_passfile) 91 | if err != nil { 92 | log.Error("password file %s doesn't exist", g_passfile) 93 | os.Exit(1) 94 | } 95 | g_pass = string(pass) 96 | } 97 | 98 | if flag.Arg(0) == "" { 99 | log.Error("Account or keyfile must be specified") 100 | os.Exit(1) 101 | } 102 | g_arg = flag.Arg(0) 103 | 104 | if flag.Arg(1) == "" { 105 | log.Error("json request file must be specified") 106 | os.Exit(1) 107 | } 108 | g_file = flag.Arg(1) 109 | 110 | // check if we have file or account 111 | var keyfile string 112 | if _, err := hexutil.Decode(g_arg); err != nil { 113 | log.Debug("input is keyfile") 114 | fi, err := os.Stat(g_arg) 115 | if err != nil { 116 | log.Error("Keyfile not found", "path", g_arg) 117 | os.Exit(1) 118 | } else if fi.IsDir() { 119 | log.Error("Keyfile argument is a directory", "path", g_arg) 120 | os.Exit(1) 121 | } 122 | keyfile = g_arg 123 | } else { 124 | log.Debug("input is account hex") 125 | fi, err := os.Stat(g_dir) 126 | if err != nil { 127 | log.Error("Keystore not found", "path", g_dir) 128 | os.Exit(1) 129 | } else if !fi.IsDir() { 130 | log.Error("Keystore is not a directory", "path", g_dir) 131 | os.Exit(1) 132 | } 133 | 134 | // search the directory for the key 135 | keystoredir := fmt.Sprintf("%s/keystore", g_dir) 136 | log.Debug("checking keystore dir", "dir", keystoredir) 137 | dircontents, err := ioutil.ReadDir(keystoredir) 138 | if err != nil { 139 | log.Error("Can't open keystore dir: %v", err) 140 | } 141 | for _, f := range dircontents { 142 | if strings.Contains(f.Name(), g_arg[2:]) { 143 | keyfile = fmt.Sprintf("%s/%s", keystoredir, f.Name()) 144 | } 145 | } 146 | } 147 | 148 | if keyfile == "" { 149 | log.Error("Account not found") 150 | os.Exit(1) 151 | } 152 | 153 | log.Info("opening account", "keyfile", keyfile) 154 | j, err := ioutil.ReadFile(keyfile) 155 | if err != nil { 156 | log.Error("cannot read file", "err", err) 157 | os.Exit(1) 158 | } 159 | if g_pass == "" { 160 | fmt.Printf("pass:") 161 | bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) 162 | if err != nil { 163 | log.Error("read password fail", "err", err) 164 | os.Exit(1) 165 | } 166 | g_pass = string(bytePassword) 167 | } 168 | log.Info("decrypting keyfile... with %s", g_pass) 169 | key, err := keystore.DecryptKey(j, g_pass) 170 | if err != nil { 171 | log.Error("key decrypt failed", "err", err) 172 | os.Exit(1) 173 | } 174 | 175 | privkeyhex := hex.EncodeToString(crypto.FromECDSA(key.PrivateKey)) 176 | log.Debug("priv", "hex", privkeyhex) 177 | privkeyregen, err := crypto.HexToECDSA(privkeyhex) 178 | if err != nil { 179 | log.Error("internal privkey conversion error", "err", err) 180 | os.Exit(1) 181 | } 182 | log.Info("ok", "privkey", privkeyhex, "address", crypto.PubkeyToAddress(privkeyregen.PublicKey)) 183 | 184 | requestJson, err := ioutil.ReadFile(g_file) 185 | if err != nil { 186 | log.Error("resultjson fail: ", "err", err.Error()) 187 | os.Exit(1) 188 | } 189 | request := &mru.Request{} 190 | err = request.UnmarshalJSON(requestJson) 191 | if err != nil { 192 | log.Error("unmarshaljson fail", "err", err.Error()) 193 | os.Exit(1) 194 | } 195 | resultJson, err := request.MarshalJSON() 196 | if err != nil { 197 | log.Error(err.Error()) 198 | os.Exit(1) 199 | } 200 | log.Info("parsed requestjson", "json", string(resultJson)) 201 | digest, err := request.GetDigest() 202 | if err != nil { 203 | log.Error(err.Error()) 204 | os.Exit(1) 205 | } 206 | log.Info("digest", "x", hexutil.Encode(digest.Bytes())) 207 | 208 | signer := mru.NewGenericSigner(key.PrivateKey) 209 | request.Sign(signer) 210 | 211 | resultJson, err = request.MarshalJSON() 212 | if err != nil { 213 | log.Error(err.Error()) 214 | os.Exit(1) 215 | } 216 | fmt.Println(string(resultJson)) 217 | } 218 | -------------------------------------------------------------------------------- /swarm/sqlite-vfs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 nolash 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /swarm/sqlite-vfs/README.md: -------------------------------------------------------------------------------- 1 | # swarm sqlite vfs 2 | 3 | This work is currently at "hello world" stage 4 | 5 | The demo does the following: 6 | 7 | * Create a new sqlite db with specified number of rows and size of blob data 8 | * Create a LocalDPA and add db to it 9 | * Prove that sqlite can call back the supplied hash used to open to the relevant go method 10 | * Seek and read from DPA according to sqlite read call 11 | * perform actual prepared (select) statement on database and output rows 12 | 13 | The `demo/main_hello.c` file and its `Makefile` are only for separate testing of the `c` part and has no other relevance. 14 | 15 | ## USAGE 16 | 17 | There are command line args. Run with `-h` to see them 18 | 19 | ## ISSUES 20 | 21 | 1. On my system I sometimes have to provide `$GOPATH` explicitly to the linker to find the right package library: 22 | 23 | ``` 24 | go run -v -ldflags "-L $GOPATH/pkg/linux_amd64" main_hello.go 25 | ``` 26 | -------------------------------------------------------------------------------- /swarm/sqlite-vfs/demo/.data.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nolash/ethereum-samples/737687b8fc9c986edaffe8a2b6ccd0aa784b4f49/swarm/sqlite-vfs/demo/.data.sql -------------------------------------------------------------------------------- /swarm/sqlite-vfs/demo/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR=./hello/ 2 | TMPNAME=./tmp 3 | TMPDIR=${TMPNAME}/ 4 | 5 | all: test 6 | 7 | test: 8 | mkdir -p ${TMPNAME} 9 | gcc -g -c -DTEST_HELLO=1 -o ${TMPDIR}hello.o ${SRCDIR}hello.c 10 | gcc -g -c -I${SRCDIR} -o ${TMPDIR}main_hello.o ./main_hello.c 11 | gcc -o hello.bin ${TMPDIR}hello.o ${TMPDIR}main_hello.o -lsqlite3 12 | rm -rf ${TMPNAME} 13 | 14 | clean: 15 | rm -f *.bin 16 | -------------------------------------------------------------------------------- /swarm/sqlite-vfs/demo/createdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dbuuid=$(`which uuidgen`) 4 | SQLITE=`which sqlite3` 5 | DBNAME="hello_${dbuuid}.sqlite3" 6 | 7 | $SQLITE $DBNAME < /dev/null 16 | $SQLITE $DBNAME < 17 | #include 18 | #include "hello.h" 19 | */ 20 | import "C" 21 | 22 | var ( 23 | vfs *C.struct_sqlite3_vfs 24 | chunkFiles []*chunkFile 25 | dpa *storage.DPA 26 | debug bool 27 | ) 28 | 29 | type chunkFile struct { 30 | reader io.ReadSeeker 31 | size int64 32 | } 33 | 34 | //export GoBzzOpen 35 | func GoBzzOpen(name *C.char, fd *C.int) C.int { 36 | hex := C.GoStringN(name, 64) // must be specific, sqlite often mangles the 0 terminator 37 | hash := common.HexToHash(hex) 38 | key := storage.Key(hash[:]) 39 | log.Debug("retrieve", "key", key, "name", name, "hex", hex) 40 | r := dpa.Retrieve(key) 41 | sz, err := r.Size(nil) 42 | if err != nil { 43 | log.Error("dpa size query fail", "err", err) 44 | return 1 45 | } 46 | chunkfile := &chunkFile{ 47 | reader: r, 48 | size: sz, 49 | } 50 | chunkFiles = append(chunkFiles, chunkfile) 51 | *fd = C.int(len(chunkFiles) - 1) 52 | return 0 53 | } 54 | 55 | //export GoBzzFileSize 56 | func GoBzzFileSize(c_fd C.int) C.longlong { 57 | c := int(c_fd) 58 | if !isValidFD(c) { 59 | return -1 60 | } 61 | log.Trace(fmt.Sprintf("reporting filesize: %d", chunkFiles[c].size)) 62 | return C.longlong(chunkFiles[c].size) 63 | } 64 | 65 | //export GoBzzRead 66 | func GoBzzRead(c_fd C.int, p unsafe.Pointer, amount C.int, offset C.longlong) int64 { 67 | 68 | // check if we have this reader 69 | c := int(c_fd) 70 | if !isValidFD(c) { 71 | return -1 72 | } 73 | 74 | // seek and retrieve from dpa 75 | file := chunkFiles[c] 76 | file.reader.Seek(int64(offset), 0) 77 | data := make([]byte, amount) 78 | c, err := file.reader.Read(data) 79 | if err != nil && err != io.EOF { 80 | log.Warn("read error", "err", err, "read", c) 81 | return -1 82 | } 83 | 84 | // not sure about this pointer handling, looks risky 85 | var pp []byte 86 | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&pp)) 87 | hdr.Len = int(amount) 88 | hdr.Data = uintptr(p) 89 | copy(pp, data[:amount]) 90 | 91 | log.Trace(fmt.Sprintf("returning data: '%x'...'%x'", data[:16], data[amount-16:amount])) 92 | return int64(len(data)) 93 | } 94 | 95 | // open database with using dpa 96 | func Open(key storage.Key) error { 97 | keystr := key.String() 98 | bzzhash := C.CString(keystr) 99 | defer C.free(unsafe.Pointer(bzzhash)) 100 | r := C.bzzvfs_open(bzzhash) 101 | if r != C.SQLITE_OK { 102 | return fmt.Errorf("sqlite open fail: %d", r) 103 | } 104 | return nil 105 | } 106 | 107 | // execute query on database using dpa 108 | func Exec(sql string) error { 109 | csql := C.CString(sql) 110 | defer C.free(unsafe.Pointer(csql)) 111 | res := make([]byte, 1024) 112 | cres := C.CString(string(res)) 113 | defer C.free(unsafe.Pointer(cres)) 114 | log.Trace(fmt.Sprintf("executing %s... ", sql)) 115 | r := C.bzzvfs_exec(C.int(len(sql)), csql, 1024, cres) 116 | if r != C.SQLITE_OK { 117 | return fmt.Errorf("sqlite exec fail (%d): %s", r, C.GoString(cres)) 118 | } 119 | return nil 120 | } 121 | 122 | // register bzz vfs 123 | func Init(newdpa *storage.DPA) error { 124 | r := C.bzzvfs_register() 125 | if r != C.SQLITE_OK { 126 | fmt.Errorf("%d", r) 127 | } 128 | dpa = newdpa 129 | if debug { 130 | C.bzzvfs_debug(1) 131 | } else { 132 | C.bzzvfs_debug(0) 133 | } 134 | return nil 135 | } 136 | 137 | func Close() { 138 | C.bzzvfs_close() 139 | } 140 | 141 | func Debug(active bool) { 142 | debug = active 143 | } 144 | 145 | func isValidFD(fd int) bool { 146 | if fd > len(chunkFiles) { 147 | return false 148 | } 149 | return true 150 | } 151 | -------------------------------------------------------------------------------- /swarm/sqlite-vfs/demo/hello/hello.h: -------------------------------------------------------------------------------- 1 | #ifndef BZZVFS_H_ 2 | #define BZZVFS_H_ 3 | #define BZZVFS_MAXPATHNAME 512 4 | #define BZZVFS_ID "bzz" 5 | #define BZZVFS_SECSIZE 4096 6 | 7 | typedef struct bzzFile{ 8 | sqlite3_file base; 9 | int fd; 10 | } bzzFile; 11 | 12 | extern int bzzvfs_register(); 13 | extern int bzzvfs_open(char*); 14 | extern int bzzvfs_exec(int, const char*, int, char*); 15 | extern void bzzvfs_debug(int b); 16 | extern void bzzvfs_close(); 17 | 18 | #endif //BZZVFS_H_ 19 | -------------------------------------------------------------------------------- /swarm/sqlite-vfs/demo/main_hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hello.h" 3 | 4 | int main(int argc, char **argv) { 5 | if (bzzvfs_register() != SQLITE_OK) { 6 | return 1; 7 | } 8 | bzzvfs_debug(1); 9 | sqlite3_vfs *thevfs = sqlite3_vfs_find(BZZVFS_ID); 10 | if (thevfs == 0) { 11 | return 1; 12 | } 13 | return bzzvfs_open("abcdef0123456789abcdef1234567890abcdef1234567890abcdef1234568790"); 14 | } 15 | -------------------------------------------------------------------------------- /trezor/README.md: -------------------------------------------------------------------------------- 1 | # trezor js sign samples 2 | 3 | Demonstrate sign of msg and tx for ethereum with custom paths using trezor 4 | 5 | ### DEPENDENCIES 6 | 7 | * ethereumjs-tx from https://github.com/ethereumjs/browser-builds 8 | * trezor-connect (v6) from https://github.com/trezor/connect 9 | 10 | _See respective subfolders for licence terms for dependencies_ 11 | -------------------------------------------------------------------------------- /trezor/lib/trezor/LICENCE.md: -------------------------------------------------------------------------------- 1 | # TREZOR REFERENCE SOURCE LICENSE (T-RSL) 2 | 3 | This license governs use of the accompanying software. If you use the software, 4 | you accept this license. If you do not accept the license, do not use the 5 | software. 6 | 7 | ## 1. Definitions 8 | 9 | The terms "reproduce," "reproduction" and "distribution" have the same meaning 10 | here as under U.S. copyright law. 11 | 12 | "You" means the licensee of the software. 13 | 14 | "Your company" means the company you worked for when you downloaded the 15 | software. 16 | 17 | "Reference use" means use of the software within your company as a reference, 18 | in read only form, for the sole purposes of debugging your products, 19 | maintaining your products, or enhancing the interoperability of your products 20 | with the software, and specifically excludes the right to distribute the 21 | software outside of your company. 22 | 23 | "Licensed patents" means any Licensor patent claims which read directly on the 24 | software as distributed by the Licensor under this license. 25 | 26 | ## 2. Grant of Rights 27 | 28 | (A) Copyright Grant - Subject to the terms of this license, the Licensor grants 29 | you a non-transferable, non-exclusive, worldwide, royalty-free copyright 30 | license to reproduce the software for reference use. 31 | 32 | (B) Patent Grant - Subject to the terms of this license, the Licensor grants 33 | you a non-transferable, non-exclusive, worldwide, royalty-free patent license 34 | under licensed patents for reference use. 35 | 36 | ## 3. Limitations 37 | 38 | (A) No Trademark License - This license does not grant you any rights to use 39 | the Licensor's name, logo, or trademarks. 40 | 41 | (B) If you begin patent litigation against the Licensor over patents that you 42 | think may apply to the software (including a cross-claim or counterclaim in 43 | a lawsuit), your license to the software ends automatically. 44 | 45 | (C) The software is licensed "as-is." You bear the risk of using it. The 46 | Licensor gives no express warranties, guarantees or conditions. You may have 47 | additional consumer rights under your local laws which this license cannot 48 | change. To the extent permitted under your local laws, the Licensor excludes 49 | the implied warranties of merchantability, fitness for a particular purpose and 50 | non-infringement. 51 | -------------------------------------------------------------------------------- /trezor/trezorsigneth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | 83 | 84 | 85 |
86 | 87 | 88 |
    89 | 90 | 91 | -------------------------------------------------------------------------------- /trezor/trezorsignmsg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | 41 | 42 | 43 |
    44 | 45 | 46 |
      47 | 48 | 49 | --------------------------------------------------------------------------------