mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 14:16:44 +00:00
SWORM - swarm poc 0.2 homestead RC1
synced up to go-ethereum 1.5.0-unstable develop branch major features: * api overhaul due to rpc v2 and node service stack interface changes * blockchain/ethereum contract interaction rewritten using abi/abigen (chequebook, ens) * swarm - cluster control CLI - migration and revamp of prehistoric eth-utils repo * poor man's end to end testing: scripted scenarios in swarm/test using swarm CLI * http proxy now handles 3 url schemes for 1) ens-enabled [bzz], 2) immutable [bzzi] and 3) raw manifest [bzzr] resolution * fixes issues with remote address setting, forwarding and syncing * new control flags to switch swap and sync on and off * placeholder basic implementation Ethereum Name Service * improved logging - now debug level is coherent regression: * uri based versioning support is dropped temporarily since state tree pruning does not guarantee historical record * registrar related functionality temporarily restricted - current ENS provides basic free and unrestricted Register/Resolve accounts/abi: * bind: repeated attempt deployment of contracts, validation against known code, transactor creation from private keys * accountmanager: getUnlocked snatch private key when unlocked cmd: * unlockAccount moved to utils/cmd and exported * getPassPhrase moved to utils/input and exported * accountcmds: reflect the change * js: GlobalRegistrar is dropped (ens) flags: * chequebook, bzzaccount, bzzport, bzzconfig, bzznoswap, bzznosync chequebook: * move from common/ to swarm/services * abigen-ised * specifies its own API (removed chequebook api from swarm/api) kademlia: * move from common to swarm/network/kademlia * address abstracted out to separate file + tests dns/ens/registrar: * moved from swarm/api to swarm/services/ens * implementation is basic placeholder before ENS is implemented * temporary rpc api via ens namespace * the old common/registrar is removed (also from eth/backend apis) swap: * the abstract swap module moved from common to swarm/services * now embedded in the swarm and chequebook specific setup (this will change) * safer chequebook deployment using abigen helpers * backend not field of swap params eth: * public accessor for GPO, needed to construct a PublicBlockChainAPI * extends ContractBackend in eth/bind.go with BalanceAt, GetTxReceipt and CodeAt API calls internal/web3ext * add js bindings for bzz, chequebook rpc apis swarm/api: * refactored api into smaller modules filesystem/storage/testapi * ethereum backend (needed for dns, swap, etc) moved to abi/bind * TODO: further refactor due to #2040 * swarm/api/http: now supports the 3 uri schemes * examples/album updated swarm/cmd: * migrate old eth-utils and modify into a cluster control CLI * bzzup now allows non-local gateway, endpoint specified as second argument swarm/network: * forwarder improved log messages, fixup condition on whether syncer is nil * hive extended with controls for testing support block read/write, swap/sync enabled/disabled * hive keepAlive launches with alarm in case no discover and no kaddb * fix IP address formatting issue [::1] -> became ::1 which refused to dial, now use discover.NewNode#String * integrate functionality for enabling/disabling sync and swap * allow nil sync state - improve syncer interface in protocol * fix SData slice out of bounds bug * fix disconnects by not replacing a node when bucket is full swarm/test * poor man's testing framework. scripts invoking swarm/cmd/swarm * added tests for basic scenarios connections, swap, sync swarm: * rewrite api using rpc v2 * blockchain comms via abi/abigen + eth.ContractBackend * integrate new flags
This commit is contained in:
parent
d736fe8efe
commit
81111e112c
85 changed files with 4849 additions and 4129 deletions
109
accounts/abi/bind/ext.go
Normal file
109
accounts/abi/bind/ext.go
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
package bind
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployOptions struct {
|
||||||
|
ReceiptQueryInterval time.Duration
|
||||||
|
DeployRetryInterval time.Duration
|
||||||
|
ConfirmationInterval time.Duration
|
||||||
|
MaxReceiptQueryAttempts int
|
||||||
|
MaxDeployAttempts int
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultDeployOptions() *DeployOptions {
|
||||||
|
return &DeployOptions{
|
||||||
|
ReceiptQueryInterval: 10000000000, // 10 sec
|
||||||
|
DeployRetryInterval: 5000000000, // 5 sec
|
||||||
|
ConfirmationInterval: 60000000000, // 60 sec
|
||||||
|
MaxReceiptQueryAttempts: 5,
|
||||||
|
MaxDeployAttempts: 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// implemented by eth.APIBackend
|
||||||
|
type Backend interface {
|
||||||
|
GetTxReceipt(txhash common.Hash) *types.Receipt
|
||||||
|
CodeAt(address common.Address) string
|
||||||
|
BalanceAt(address common.Address) *big.Int
|
||||||
|
ContractBackend
|
||||||
|
}
|
||||||
|
|
||||||
|
func Deploy(deployF func(*TransactOpts, ContractBackend) (*types.Transaction, error), contractCode string, deployTransactor *TransactOpts, opt *DeployOptions, backend Backend) (contractAddr common.Address, err error) {
|
||||||
|
|
||||||
|
deployRetryTimer := time.NewTimer(0).C
|
||||||
|
var receiptQueryTimer <-chan time.Time
|
||||||
|
var deployRetries, receiptQueries int
|
||||||
|
var txhash common.Hash
|
||||||
|
|
||||||
|
DEPLOY:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-deployRetryTimer:
|
||||||
|
deployRetries++
|
||||||
|
if deployRetries == opt.MaxDeployAttempts {
|
||||||
|
return common.Address{}, fmt.Errorf("deployment failed...giving up after %v attempts", opt.MaxDeployAttempts)
|
||||||
|
}
|
||||||
|
tx, err := deployF(deployTransactor, backend)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(logger.Warn).Infof("deployment failed: %v (attempt %v)", err, deployRetries)
|
||||||
|
deployRetryTimer = time.NewTimer(opt.DeployRetryInterval).C
|
||||||
|
continue DEPLOY
|
||||||
|
}
|
||||||
|
|
||||||
|
txhash = tx.Hash()
|
||||||
|
deployRetryTimer = nil
|
||||||
|
receiptQueryTimer = time.NewTimer(0).C
|
||||||
|
|
||||||
|
case <-receiptQueryTimer:
|
||||||
|
receipt := backend.GetTxReceipt(txhash)
|
||||||
|
receiptQueries++
|
||||||
|
if receipt == nil {
|
||||||
|
if receiptQueries == opt.MaxReceiptQueryAttempts {
|
||||||
|
glog.V(logger.Warn).Infof("attempt %s deployment failed. Given up after %v attempts", opt.MaxReceiptQueryAttempts)
|
||||||
|
|
||||||
|
deployRetryTimer = time.NewTimer(opt.DeployRetryInterval).C
|
||||||
|
receiptQueryTimer = nil
|
||||||
|
continue DEPLOY
|
||||||
|
|
||||||
|
}
|
||||||
|
glog.V(logger.Detail).Infof("new checkbook contract (txhash: %v) not yet mined... checking in %v", txhash.Hex(), opt.ReceiptQueryInterval)
|
||||||
|
receiptQueryTimer = time.NewTimer(opt.ReceiptQueryInterval).C
|
||||||
|
continue DEPLOY
|
||||||
|
}
|
||||||
|
|
||||||
|
contractAddr = receipt.ContractAddress
|
||||||
|
glog.V(logger.Detail).Infof("new chequebook contract mined at %v (owner: %v)", contractAddr.Hex(), deployTransactor.From.Hex())
|
||||||
|
<-time.NewTimer(opt.ConfirmationInterval).C
|
||||||
|
err = Validate(contractAddr, contractCode, backend)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(logger.Warn).Infof("invalid contract at %v after %v: %v", contractAddr.Hex(), opt.ConfirmationInterval, err)
|
||||||
|
deployRetryTimer = time.NewTimer(opt.DeployRetryInterval).C
|
||||||
|
receiptQueryTimer = nil
|
||||||
|
continue DEPLOY
|
||||||
|
}
|
||||||
|
break DEPLOY
|
||||||
|
|
||||||
|
} // select
|
||||||
|
} // for
|
||||||
|
return contractAddr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Validate(contractAddr common.Address, expCode string, backend Backend) (err error) {
|
||||||
|
if (contractAddr == common.Address{}) {
|
||||||
|
return fmt.Errorf("zero address")
|
||||||
|
}
|
||||||
|
code := backend.CodeAt(contractAddr)
|
||||||
|
if len(expCode) > 0 && code != expCode {
|
||||||
|
return fmt.Errorf("incorrect code %v:\n%v\n%v", contractAddr.Hex(), code, expCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -63,7 +63,7 @@ import a private key into a new account.
|
||||||
It supports interactive mode, when you are prompted for password as well as
|
It supports interactive mode, when you are prompted for password as well as
|
||||||
non-interactive mode where passwords are supplied via a given password file.
|
non-interactive mode where passwords are supplied via a given password file.
|
||||||
Non-interactive mode is only meant for scripted use on test networks or known
|
Non-interactive mode is only meant for scripted use on test networks or known
|
||||||
safe environments.
|
safe environ>>>ments.
|
||||||
|
|
||||||
Make sure you remember the password you gave when creating a new account (with
|
Make sure you remember the password you gave when creating a new account (with
|
||||||
either new or import). Without it you are not able to unlock your account.
|
either new or import). Without it you are not able to unlock your account.
|
||||||
|
|
@ -119,7 +119,7 @@ password to file or expose in any other way.
|
||||||
Description: `
|
Description: `
|
||||||
|
|
||||||
ethereum account update <address>
|
ethereum account update <address>
|
||||||
|
>>>>>>>>>>>>
|
||||||
Update an existing account.
|
Update an existing account.
|
||||||
|
|
||||||
The account is saved in the newest version in encrypted format, you are prompted
|
The account is saved in the newest version in encrypted format, you are prompted
|
||||||
|
|
@ -175,7 +175,7 @@ func accountList(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// tries unlocking the specified account a few times.
|
// tries unlocking the specifiedqqa few times.
|
||||||
func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
|
func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
|
||||||
account, err := utils.MakeAddress(accman, address)
|
account, err := utils.MakeAddress(accman, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -203,7 +203,7 @@ func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i
|
||||||
return accounts.Account{}, ""
|
return accounts.Account{}, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPassPhrase retrieves the passwor associated with an account, either fetched
|
// getPassPhrase retrieves the password associated with an account, either fetched
|
||||||
// from a list of preloaded passphrases, or requested interactively from the user.
|
// from a list of preloaded passphrases, or requested interactively from the user.
|
||||||
func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
|
func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
|
||||||
// If a list of passwords was supplied, retrieve from them
|
// If a list of passwords was supplied, retrieve from them
|
||||||
|
|
@ -262,7 +262,7 @@ func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrErro
|
||||||
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
||||||
func accountCreate(ctx *cli.Context) error {
|
func accountCreate(ctx *cli.Context) error {
|
||||||
accman := utils.MakeAccountManager(ctx)
|
accman := utils.MakeAccountManager(ctx)
|
||||||
password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
password := utils.GetPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
account, err := accman.NewAccount(password)
|
account, err := accman.NewAccount(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -280,8 +280,8 @@ func accountUpdate(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
accman := utils.MakeAccountManager(ctx)
|
accman := utils.MakeAccountManager(ctx)
|
||||||
|
|
||||||
account, oldPassword := unlockAccount(ctx, accman, ctx.Args().First(), 0, nil)
|
account, oldPassword := utils.UnlockAccount(accman, ctx.Args().First(), 0, nil)
|
||||||
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
|
newPassword := utils.GetPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
|
||||||
if err := accman.Update(account, oldPassword, newPassword); err != nil {
|
if err := accman.Update(account, oldPassword, newPassword); err != nil {
|
||||||
utils.Fatalf("Could not update the account: %v", err)
|
utils.Fatalf("Could not update the account: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -299,7 +299,7 @@ func importWallet(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
accman := utils.MakeAccountManager(ctx)
|
accman := utils.MakeAccountManager(ctx)
|
||||||
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
|
passphrase := utils.GetPassPhrase("", false, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
acct, err := accman.ImportPreSaleKey(keyJson, passphrase)
|
acct, err := accman.ImportPreSaleKey(keyJson, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -319,7 +319,7 @@ func accountImport(ctx *cli.Context) error {
|
||||||
utils.Fatalf("Failed to load the private key: %v", err)
|
utils.Fatalf("Failed to load the private key: %v", err)
|
||||||
}
|
}
|
||||||
accman := utils.MakeAccountManager(ctx)
|
accman := utils.MakeAccountManager(ctx)
|
||||||
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
passphrase := utils.GetPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||||
acct, err := accman.ImportECDSA(key, passphrase)
|
acct, err := accman.ImportECDSA(key, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not create the account: %v", err)
|
utils.Fatalf("Could not create the account: %v", err)
|
||||||
|
|
|
||||||
424
cmd/geth/js.go
Normal file
424
cmd/geth/js.go
Normal file
|
|
@ -0,0 +1,424 @@
|
||||||
|
// Copyright 2015 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/web3ext"
|
||||||
|
re "github.com/ethereum/go-ethereum/jsre"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/peterh/liner"
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
passwordRegexp = regexp.MustCompile("personal.[nu]")
|
||||||
|
leadingSpace = regexp.MustCompile("^ ")
|
||||||
|
onlyws = regexp.MustCompile("^\\s*$")
|
||||||
|
exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$")
|
||||||
|
)
|
||||||
|
|
||||||
|
type jsre struct {
|
||||||
|
re *re.JSRE
|
||||||
|
stack *node.Node
|
||||||
|
wait chan *big.Int
|
||||||
|
ps1 string
|
||||||
|
atexit func()
|
||||||
|
corsDomain string
|
||||||
|
client rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCompleter(re *jsre) liner.WordCompleter {
|
||||||
|
return func(line string, pos int) (head string, completions []string, tail string) {
|
||||||
|
if len(line) == 0 || pos == 0 {
|
||||||
|
return "", nil, ""
|
||||||
|
}
|
||||||
|
// chuck data to relevant part for autocompletion, e.g. in case of nested lines eth.getBalance(eth.coinb<tab><tab>
|
||||||
|
i := 0
|
||||||
|
for i = pos - 1; i > 0; i-- {
|
||||||
|
if line[i] == '.' || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i >= 3 && line[i] == '3' && line[i-3] == 'w' && line[i-2] == 'e' && line[i-1] == 'b' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return line[:i], re.re.CompleteKeywords(line[i:pos]), line[pos:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLightweightJSRE(docRoot string, client rpc.Client, datadir string, interactive bool) *jsre {
|
||||||
|
js := &jsre{ps1: "> "}
|
||||||
|
js.wait = make(chan *big.Int)
|
||||||
|
js.client = client
|
||||||
|
js.re = re.New(docRoot)
|
||||||
|
if err := js.apiBindings(); err != nil {
|
||||||
|
utils.Fatalf("Unable to initialize console - %v", err)
|
||||||
|
}
|
||||||
|
js.setupInput(datadir)
|
||||||
|
return js
|
||||||
|
}
|
||||||
|
|
||||||
|
func newJSRE(stack *node.Node, docRoot, corsDomain string, client rpc.Client, interactive bool) *jsre {
|
||||||
|
js := &jsre{stack: stack, ps1: "> "}
|
||||||
|
// set default cors domain used by startRpc from CLI flag
|
||||||
|
js.corsDomain = corsDomain
|
||||||
|
js.wait = make(chan *big.Int)
|
||||||
|
js.client = client
|
||||||
|
js.re = re.New(docRoot)
|
||||||
|
if err := js.apiBindings(); err != nil {
|
||||||
|
utils.Fatalf("Unable to connect - %v", err)
|
||||||
|
}
|
||||||
|
js.setupInput(stack.DataDir())
|
||||||
|
return js
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) setupInput(datadir string) {
|
||||||
|
self.withHistory(datadir, func(hist *os.File) { utils.Stdin.ReadHistory(hist) })
|
||||||
|
utils.Stdin.SetCtrlCAborts(true)
|
||||||
|
utils.Stdin.SetWordCompleter(makeCompleter(self))
|
||||||
|
utils.Stdin.SetTabCompletionStyle(liner.TabPrints)
|
||||||
|
self.atexit = func() {
|
||||||
|
self.withHistory(datadir, func(hist *os.File) {
|
||||||
|
hist.Truncate(0)
|
||||||
|
utils.Stdin.WriteHistory(hist)
|
||||||
|
})
|
||||||
|
utils.Stdin.Close()
|
||||||
|
close(self.wait)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) batch(statement string) {
|
||||||
|
err := self.re.EvalAndPrettyPrint(statement)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%v", jsErrorString(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.atexit != nil {
|
||||||
|
self.atexit()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.re.Stop(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show summary of current geth instance
|
||||||
|
func (self *jsre) welcome() {
|
||||||
|
self.re.Run(`
|
||||||
|
(function () {
|
||||||
|
console.log('instance: ' + web3.version.node);
|
||||||
|
console.log("coinbase: " + eth.coinbase);
|
||||||
|
var ts = 1000 * eth.getBlock(eth.blockNumber).timestamp;
|
||||||
|
console.log("at block: " + eth.blockNumber + " (" + new Date(ts) + ")");
|
||||||
|
console.log(' datadir: ' + admin.datadir);
|
||||||
|
})();
|
||||||
|
`)
|
||||||
|
if modules, err := self.supportedApis(); err == nil {
|
||||||
|
loadedModules := make([]string, 0)
|
||||||
|
for api, version := range modules {
|
||||||
|
loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version))
|
||||||
|
}
|
||||||
|
sort.Strings(loadedModules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) supportedApis() (map[string]string, error) {
|
||||||
|
return self.client.SupportedModules()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (js *jsre) apiBindings() error {
|
||||||
|
apis, err := js.supportedApis()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
apiNames := make([]string, 0, len(apis))
|
||||||
|
for a, _ := range apis {
|
||||||
|
apiNames = append(apiNames, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
jeth := utils.NewJeth(js.re, js.client)
|
||||||
|
js.re.Set("jeth", struct{}{})
|
||||||
|
t, _ := js.re.Get("jeth")
|
||||||
|
jethObj := t.Object()
|
||||||
|
|
||||||
|
jethObj.Set("send", jeth.Send)
|
||||||
|
jethObj.Set("sendAsync", jeth.Send)
|
||||||
|
|
||||||
|
err = js.re.Compile("bignumber.js", re.BigNumber_JS)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error loading bignumber.js: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = js.re.Compile("web3.js", re.Web3_JS)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error loading web3.js: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = js.re.Run("var Web3 = require('web3');")
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error requiring web3: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = js.re.Run("var web3 = new Web3(jeth);")
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error setting web3 provider: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// load only supported API's in javascript runtime
|
||||||
|
shortcuts := "var eth = web3.eth; var personal = web3.personal; "
|
||||||
|
for _, apiName := range apiNames {
|
||||||
|
if apiName == "web3" || apiName == "rpc" {
|
||||||
|
continue // manually mapped or ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if jsFile, ok := web3ext.Modules[apiName]; ok {
|
||||||
|
if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), jsFile); err == nil {
|
||||||
|
shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName)
|
||||||
|
} else {
|
||||||
|
utils.Fatalf("Error loading %s.js: %v", apiName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = js.re.Run(shortcuts)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error setting namespaces: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// overrule some of the methods that require password as input and ask for it interactively
|
||||||
|
p, err := js.re.Get("personal")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Unable to overrule sensitive methods in personal module")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override the unlockAccount and newAccount methods on the personal object since these require user interaction.
|
||||||
|
// Assign the jeth.unlockAccount and jeth.newAccount in the jsre the original web3 callbacks. These will be called
|
||||||
|
// by the jeth.* methods after they got the password from the user and send the original web3 request to the backend.
|
||||||
|
if persObj := p.Object(); persObj != nil { // make sure the personal api is enabled over the interface
|
||||||
|
js.re.Run(`jeth.unlockAccount = personal.unlockAccount;`)
|
||||||
|
persObj.Set("unlockAccount", jeth.UnlockAccount)
|
||||||
|
js.re.Run(`jeth.newAccount = personal.newAccount;`)
|
||||||
|
persObj.Set("newAccount", jeth.NewAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer.
|
||||||
|
// Bind these if the admin module is available.
|
||||||
|
if a, err := js.re.Get("admin"); err == nil {
|
||||||
|
if adminObj := a.Object(); adminObj != nil {
|
||||||
|
adminObj.Set("sleepBlocks", jeth.SleepBlocks)
|
||||||
|
adminObj.Set("sleep", jeth.Sleep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) AskPassword() (string, bool) {
|
||||||
|
pass, err := utils.Stdin.PasswordPrompt("Passphrase: ")
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return pass, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) ConfirmTransaction(tx string) bool {
|
||||||
|
// Retrieve the Ethereum instance from the node
|
||||||
|
var ethereum *eth.Ethereum
|
||||||
|
if err := self.stack.Service(ðereum); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// If natspec is enabled, ask for permission
|
||||||
|
if ethereum.NatSpec && false /* disabled for now */ {
|
||||||
|
// notice := natspec.GetNotice(self.xeth, tx, ethereum.HTTPClient())
|
||||||
|
// fmt.Println(notice)
|
||||||
|
// answer, _ := self.Prompt("Confirm Transaction [y/n]")
|
||||||
|
// return strings.HasPrefix(strings.Trim(answer, " "), "y")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) UnlockAccount(addr []byte) bool {
|
||||||
|
fmt.Printf("Please unlock account %x.\n", addr)
|
||||||
|
pass, err := utils.Stdin.PasswordPrompt("Passphrase: ")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// TODO: allow retry
|
||||||
|
var ethereum *eth.Ethereum
|
||||||
|
if err := self.stack.Service(ðereum); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
a := accounts.Account{Address: common.BytesToAddress(addr)}
|
||||||
|
if err := ethereum.AccountManager().Unlock(a, pass); err != nil {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
fmt.Println("Account is now unlocked for this session.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// preloadJSFiles loads JS files that the user has specified with ctx.PreLoadJSFlag into
|
||||||
|
// the JSRE. If not all files could be loaded it will return an error describing the error.
|
||||||
|
func (self *jsre) preloadJSFiles(ctx *cli.Context) error {
|
||||||
|
if ctx.GlobalString(utils.PreLoadJSFlag.Name) != "" {
|
||||||
|
assetPath := ctx.GlobalString(utils.JSpathFlag.Name)
|
||||||
|
jsFiles := strings.Split(ctx.GlobalString(utils.PreLoadJSFlag.Name), ",")
|
||||||
|
for _, file := range jsFiles {
|
||||||
|
filename := common.AbsolutePath(assetPath, strings.TrimSpace(file))
|
||||||
|
if err := self.re.Exec(filename); err != nil {
|
||||||
|
return fmt.Errorf("%s: %v", file, jsErrorString(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// jsErrorString adds a backtrace to errors generated by otto.
|
||||||
|
func jsErrorString(err error) string {
|
||||||
|
if ottoErr, ok := err.(*otto.Error); ok {
|
||||||
|
return ottoErr.String()
|
||||||
|
}
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) interactive() {
|
||||||
|
// Read input lines.
|
||||||
|
prompt := make(chan string)
|
||||||
|
inputln := make(chan string)
|
||||||
|
go func() {
|
||||||
|
defer close(inputln)
|
||||||
|
for {
|
||||||
|
line, err := utils.Stdin.Prompt(<-prompt)
|
||||||
|
if err != nil {
|
||||||
|
if err == liner.ErrPromptAborted { // ctrl-C
|
||||||
|
self.resetPrompt()
|
||||||
|
inputln <- ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
inputln <- line
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Wait for Ctrl-C, too.
|
||||||
|
sig := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sig, os.Interrupt)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if self.atexit != nil {
|
||||||
|
self.atexit()
|
||||||
|
}
|
||||||
|
self.re.Stop(false)
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
prompt <- self.ps1
|
||||||
|
select {
|
||||||
|
case <-sig:
|
||||||
|
fmt.Println("caught interrupt, exiting")
|
||||||
|
return
|
||||||
|
case input, ok := <-inputln:
|
||||||
|
if !ok || indentCount <= 0 && exit.MatchString(input) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if onlyws.MatchString(input) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
str += input + "\n"
|
||||||
|
self.setIndent()
|
||||||
|
if indentCount <= 0 {
|
||||||
|
if mustLogInHistory(str) {
|
||||||
|
utils.Stdin.AppendHistory(str[:len(str)-1])
|
||||||
|
}
|
||||||
|
self.parseInput(str)
|
||||||
|
str = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustLogInHistory(input string) bool {
|
||||||
|
return len(input) == 0 ||
|
||||||
|
passwordRegexp.MatchString(input) ||
|
||||||
|
!leadingSpace.MatchString(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) withHistory(datadir string, op func(*os.File)) {
|
||||||
|
hist, err := os.OpenFile(filepath.Join(datadir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("unable to open history file: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
op(hist)
|
||||||
|
hist.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) parseInput(code string) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Println("[native] error", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err := self.re.EvalAndPrettyPrint(code); err != nil {
|
||||||
|
if ottoErr, ok := err.(*otto.Error); ok {
|
||||||
|
fmt.Println(ottoErr.String())
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var indentCount = 0
|
||||||
|
var str = ""
|
||||||
|
|
||||||
|
func (self *jsre) resetPrompt() {
|
||||||
|
indentCount = 0
|
||||||
|
str = ""
|
||||||
|
self.ps1 = "> "
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *jsre) setIndent() {
|
||||||
|
open := strings.Count(str, "{")
|
||||||
|
open += strings.Count(str, "(")
|
||||||
|
closed := strings.Count(str, "}")
|
||||||
|
closed += strings.Count(str, ")")
|
||||||
|
indentCount = open - closed
|
||||||
|
if indentCount <= 0 {
|
||||||
|
self.ps1 = "> "
|
||||||
|
} else {
|
||||||
|
self.ps1 = strings.Join(make([]string, indentCount*2), "..")
|
||||||
|
self.ps1 += " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
var port = 8500
|
|
||||||
|
|
||||||
func bzzREPL(t *testing.T, configf func(*api.Config)) (string, string, *testjethre, *node.Node) {
|
|
||||||
prvKey, err := crypto.GenerateKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("unable to generate key")
|
|
||||||
}
|
|
||||||
bzztmp, err := ioutil.TempDir("", "bzz-js-test")
|
|
||||||
config, err := api.NewConfig(bzztmp, common.Address{}, prvKey)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("unable to configure swarm")
|
|
||||||
}
|
|
||||||
if configf != nil {
|
|
||||||
configf(config)
|
|
||||||
}
|
|
||||||
tmp, repl, stack := testREPL(t, func(n *node.Node) {
|
|
||||||
if err := n.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
|
||||||
return swarm.NewSwarm(ctx, config, false)
|
|
||||||
}); err != nil {
|
|
||||||
t.Fatalf("Failed to register the Swarm service: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return bzztmp, tmp, repl, stack
|
|
||||||
}
|
|
||||||
|
|
||||||
func withREPL(t *testing.T, cf func(*api.Config), f func(repl *testjethre)) {
|
|
||||||
bzztmp, tmp, repl, stack := bzzREPL(t, cf)
|
|
||||||
defer stack.Stop()
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
defer os.RemoveAll(bzztmp)
|
|
||||||
f(repl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBzzPutGet(t *testing.T) {
|
|
||||||
withREPL(t,
|
|
||||||
func(c *api.Config) {
|
|
||||||
c.Port = ""
|
|
||||||
}, func(repl *testjethre) {
|
|
||||||
if checkEvalJSON(t, repl, `hash = bzz.put("console.log(\"hello from console\")", "application/javascript")`, `"97f1b7c7ea12468fd37c262383b9aa862d0cfbc4fc7218652374679fc5cf40cd"`) != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
want := `{"content":"console.log(\"hello from console\")","contentType":"application/javascript","size":"33","status":"0"}`
|
|
||||||
if checkEvalJSON(t, repl, `bzz.get(hash)`, want) != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// the server can be initialized only once per test session !
|
|
||||||
// until we implement a stoppable http server
|
|
||||||
// further http tests will need to make sure the correct server is running
|
|
||||||
func TestHTTP(t *testing.T) {
|
|
||||||
withREPL(t, nil, func(repl *testjethre) {
|
|
||||||
if checkEvalJSON(t, repl, `hash = bzz.put("f42 = function() { return 42 }", "application/javascript")`, `"e6847876f00102441f850b2d438a06d10e3bf24e6a0a76d47b073a86c3c2f9ac"`) != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if checkEvalJSON(t, repl, `admin.httpGet("bzz://"+hash)`, `"f42 = function() { return 42 }"`) != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// if checkEvalJSON(t, repl, `http.loadScript("bzz://"+hash)`, `true`) != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if checkEvalJSON(t, repl, `f42()`, `42`) != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
"github.com/ethereum/go-ethereum/common/compiler"
|
||||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||||
|
|
@ -43,18 +42,17 @@ import (
|
||||||
const (
|
const (
|
||||||
testSolcPath = ""
|
testSolcPath = ""
|
||||||
solcVersion = "0.9.23"
|
solcVersion = "0.9.23"
|
||||||
|
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||||
testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674"
|
testBalance = "10000000000000000000"
|
||||||
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
|
||||||
testBalance = "10000000000000000000"
|
|
||||||
// of empty string
|
// of empty string
|
||||||
testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
|
versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
|
||||||
testNodeKey = crypto.ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f"))
|
testNodeKey, _ = crypto.HexToECDSA("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")
|
||||||
testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
|
testAccount, _ = crypto.HexToECDSA("e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674")
|
||||||
|
testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
|
||||||
)
|
)
|
||||||
|
|
||||||
type testjethre struct {
|
type testjethre struct {
|
||||||
|
|
@ -63,17 +61,6 @@ type testjethre struct {
|
||||||
client *httpclient.HTTPClient
|
client *httpclient.HTTPClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *testjethre) UnlockAccount(acc []byte) bool {
|
|
||||||
var ethereum *eth.Ethereum
|
|
||||||
self.stack.Service(ðereum)
|
|
||||||
|
|
||||||
err := ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "")
|
|
||||||
if err != nil {
|
|
||||||
panic("unable to unlock")
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporary disabled while natspec hasn't been migrated
|
// Temporary disabled while natspec hasn't been migrated
|
||||||
//func (self *testjethre) ConfirmTransaction(tx string) bool {
|
//func (self *testjethre) ConfirmTransaction(tx string) bool {
|
||||||
// var ethereum *eth.Ethereum
|
// var ethereum *eth.Ethereum
|
||||||
|
|
@ -95,18 +82,19 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Create a networkless protocol stack
|
// Create a networkless protocol stack
|
||||||
stack, err := node.New(&node.Config{PrivateKey: testNodeKey, Name: "test", NoDiscovery: true})
|
stack, err := node.New(&node.Config{DataDir: tmp, PrivateKey: testNodeKey, Name: "test", NoDiscovery: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create node: %v", err)
|
t.Fatalf("failed to create node: %v", err)
|
||||||
}
|
}
|
||||||
// Initialize and register the Ethereum protocol
|
// Initialize and register the Ethereum protocol
|
||||||
keystore := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
|
accman := accounts.NewPlaintextManager(filepath.Join(tmp, "keystore"))
|
||||||
accman := accounts.NewManager(keystore)
|
|
||||||
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)})
|
core.WriteGenesisBlockForTesting(db, core.GenesisAccount{
|
||||||
|
Address: common.HexToAddress(testAddress),
|
||||||
|
Balance: common.String2Big(testBalance),
|
||||||
|
})
|
||||||
ethConf := ð.Config{
|
ethConf := ð.Config{
|
||||||
|
ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)},
|
||||||
TestGenesisState: db,
|
TestGenesisState: db,
|
||||||
AccountManager: accman,
|
AccountManager: accman,
|
||||||
DocRoot: "/",
|
DocRoot: "/",
|
||||||
|
|
@ -122,15 +110,11 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
|
||||||
t.Fatalf("failed to register ethereum protocol: %v", err)
|
t.Fatalf("failed to register ethereum protocol: %v", err)
|
||||||
}
|
}
|
||||||
// Initialize all the keys for testing
|
// Initialize all the keys for testing
|
||||||
keyb, err := crypto.HexToECDSA(testKey)
|
a, err := accman.ImportECDSA(testAccount, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
key := crypto.NewKeyFromECDSA(keyb)
|
if err := accman.Unlock(a, ""); err != nil {
|
||||||
if err := keystore.StoreKey(key, ""); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := accman.Unlock(key.Address, ""); err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Start the node and assemble the REPL tester
|
// Start the node and assemble the REPL tester
|
||||||
|
|
@ -141,8 +125,10 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
|
||||||
stack.Service(ðereum)
|
stack.Service(ðereum)
|
||||||
|
|
||||||
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
|
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
|
||||||
//client := comms.NewInProcClient(codec.JSON)
|
client, err := stack.Attach()
|
||||||
client := utils.NewInProcRPCClient(stack)
|
if err != nil {
|
||||||
|
t.Fatalf("failed to attach to node: %v", err)
|
||||||
|
}
|
||||||
tf := &testjethre{client: ethereum.HTTPClient()}
|
tf := &testjethre{client: ethereum.HTTPClient()}
|
||||||
repl := newJSRE(stack, assetPath, "", client, false)
|
repl := newJSRE(stack, assetPath, "", client, false)
|
||||||
tf.jsre = repl
|
tf.jsre = repl
|
||||||
|
|
@ -152,9 +138,6 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
|
||||||
func TestNodeInfo(t *testing.T) {
|
func TestNodeInfo(t *testing.T) {
|
||||||
t.Skip("broken after p2p update")
|
t.Skip("broken after p2p update")
|
||||||
tmp, repl, ethereum := testJEthRE(t)
|
tmp, repl, ethereum := testJEthRE(t)
|
||||||
if err := ethereum.Start(); err != nil {
|
|
||||||
t.Fatalf("error starting ethereum: %v", err)
|
|
||||||
}
|
|
||||||
defer ethereum.Stop()
|
defer ethereum.Stop()
|
||||||
defer os.RemoveAll(tmp)
|
defer os.RemoveAll(tmp)
|
||||||
|
|
||||||
|
|
@ -398,7 +381,7 @@ multiply7 = Multiply7.at(contractaddress);
|
||||||
if sol != nil && solcVersion != sol.Version() {
|
if sol != nil && solcVersion != sol.Version() {
|
||||||
modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`))
|
modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`))
|
||||||
fmt.Printf("modified contractinfo:\n%s\n", modContractInfo)
|
fmt.Printf("modified contractinfo:\n%s\n", modContractInfo)
|
||||||
contentHash = `"` + common.ToHex(crypto.Sha3([]byte(modContractInfo))) + `"`
|
contentHash = `"` + common.ToHex(crypto.Keccak256([]byte(modContractInfo))) + `"`
|
||||||
}
|
}
|
||||||
if checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) != nil {
|
if checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) != nil {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
|
@ -190,9 +190,6 @@ participating.
|
||||||
utils.ExecFlag,
|
utils.ExecFlag,
|
||||||
utils.PreloadJSFlag,
|
utils.PreloadJSFlag,
|
||||||
utils.WhisperEnabledFlag,
|
utils.WhisperEnabledFlag,
|
||||||
utils.SwarmConfigPathFlag,
|
|
||||||
utils.SwarmAccountAddrFlag,
|
|
||||||
utils.ChequebookAddrFlag,
|
|
||||||
utils.DevModeFlag,
|
utils.DevModeFlag,
|
||||||
utils.TestNetFlag,
|
utils.TestNetFlag,
|
||||||
utils.VMForceJitFlag,
|
utils.VMForceJitFlag,
|
||||||
|
|
@ -210,6 +207,13 @@ participating.
|
||||||
utils.GpobaseStepUpFlag,
|
utils.GpobaseStepUpFlag,
|
||||||
utils.GpobaseCorrectionFactorFlag,
|
utils.GpobaseCorrectionFactorFlag,
|
||||||
utils.ExtraDataFlag,
|
utils.ExtraDataFlag,
|
||||||
|
// on top of develop
|
||||||
|
utils.SwarmConfigPathFlag,
|
||||||
|
utils.SwarmSwapDisabled,
|
||||||
|
utils.SwarmSyncDisabled,
|
||||||
|
utils.SwarmPortFlag,
|
||||||
|
utils.SwarmAccountAddrFlag,
|
||||||
|
utils.ChequebookAddrFlag,
|
||||||
}
|
}
|
||||||
app.Flags = append(app.Flags, debug.Flags...)
|
app.Flags = append(app.Flags, debug.Flags...)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -209,3 +210,57 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
||||||
glog.Infoln("Exported blockchain to ", fn)
|
glog.Infoln("Exported blockchain to ", fn)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tries unlocking the specified account a few times.
|
||||||
|
func UnlockAccount(accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
|
||||||
|
account, err := MakeAddress(accman, address)
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Could not list accounts: %v", err)
|
||||||
|
}
|
||||||
|
for trials := 0; trials < 3; trials++ {
|
||||||
|
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
|
||||||
|
password := GetPassPhrase(prompt, false, i, passwords)
|
||||||
|
err = accman.Unlock(account, password)
|
||||||
|
if err == nil {
|
||||||
|
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
|
||||||
|
return account, password
|
||||||
|
}
|
||||||
|
if err, ok := err.(*accounts.AmbiguousAddrError); ok {
|
||||||
|
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
|
||||||
|
return ambiguousAddrRecovery(accman, err, password), password
|
||||||
|
}
|
||||||
|
if err != accounts.ErrDecrypt {
|
||||||
|
// No need to prompt again if the error is not decryption-related.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// All trials expended to unlock account, bail out
|
||||||
|
Fatalf("Failed to unlock account %s (%v)", address, err)
|
||||||
|
return accounts.Account{}, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func ambiguousAddrRecovery(am *accounts.Manager, err *accounts.AmbiguousAddrError, auth string) accounts.Account {
|
||||||
|
fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
|
||||||
|
for _, a := range err.Matches {
|
||||||
|
fmt.Println(" ", a.File)
|
||||||
|
}
|
||||||
|
fmt.Println("Testing your passphrase against all of them...")
|
||||||
|
var match *accounts.Account
|
||||||
|
for _, a := range err.Matches {
|
||||||
|
if err := am.Unlock(a, auth); err == nil {
|
||||||
|
match = &a
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match == nil {
|
||||||
|
Fatalf("None of the listed files could be unlocked.")
|
||||||
|
}
|
||||||
|
fmt.Printf("Your passphrase unlocked %s\n", match.File)
|
||||||
|
fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
|
||||||
|
for _, a := range err.Matches {
|
||||||
|
if a != *match {
|
||||||
|
fmt.Println(" ", a.File)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *match
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
"github.com/ethereum/go-ethereum/release"
|
"github.com/ethereum/go-ethereum/release"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/rpc/api"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/comms"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/useragent"
|
|
||||||
rpc "github.com/ethereum/go-ethereum/rpc/v2"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm"
|
"github.com/ethereum/go-ethereum/swarm"
|
||||||
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
||||||
"github.com/ethereum/go-ethereum/whisper"
|
"github.com/ethereum/go-ethereum/whisper"
|
||||||
|
|
@ -357,22 +351,6 @@ var (
|
||||||
Name: "shh",
|
Name: "shh",
|
||||||
Usage: "Enable Whisper",
|
Usage: "Enable Whisper",
|
||||||
}
|
}
|
||||||
ChequebookAddrFlag = cli.StringFlag{
|
|
||||||
Name: "chequebook",
|
|
||||||
Usage: "chequebook contract address",
|
|
||||||
}
|
|
||||||
SwarmAccountAddrFlag = cli.StringFlag{
|
|
||||||
Name: "bzzaccount",
|
|
||||||
Usage: "Swarm account address (swarm disabled if empty)",
|
|
||||||
}
|
|
||||||
SwarmConfigPathFlag = cli.StringFlag{
|
|
||||||
Name: "bzzconfig",
|
|
||||||
Usage: "Swarm config file path (datadir/bzz)",
|
|
||||||
}
|
|
||||||
SwarmSwapDisabled = cli.BoolFlag{
|
|
||||||
Name: "bzznoswap",
|
|
||||||
Usage: "Swarm SWAP disabled (false)",
|
|
||||||
}
|
|
||||||
// ATM the url is left to the user and deployment to
|
// ATM the url is left to the user and deployment to
|
||||||
JSpathFlag = cli.StringFlag{
|
JSpathFlag = cli.StringFlag{
|
||||||
Name: "jspath",
|
Name: "jspath",
|
||||||
|
|
@ -416,6 +394,32 @@ var (
|
||||||
Usage: "Suggested gas price base correction factor (%)",
|
Usage: "Suggested gas price base correction factor (%)",
|
||||||
Value: 110,
|
Value: 110,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on top of develop
|
||||||
|
ChequebookAddrFlag = cli.StringFlag{
|
||||||
|
Name: "chequebook",
|
||||||
|
Usage: "chequebook contract address",
|
||||||
|
}
|
||||||
|
SwarmAccountAddrFlag = cli.StringFlag{
|
||||||
|
Name: "bzzaccount",
|
||||||
|
Usage: "Swarm account address (swarm disabled if empty)",
|
||||||
|
}
|
||||||
|
SwarmPortFlag = cli.StringFlag{
|
||||||
|
Name: "bzzport",
|
||||||
|
Usage: "Swarm local http api port",
|
||||||
|
}
|
||||||
|
SwarmConfigPathFlag = cli.StringFlag{
|
||||||
|
Name: "bzzconfig",
|
||||||
|
Usage: "Swarm config file path (datadir/bzz)",
|
||||||
|
}
|
||||||
|
SwarmSwapDisabled = cli.BoolFlag{
|
||||||
|
Name: "bzznoswap",
|
||||||
|
Usage: "Swarm SWAP disabled (false)",
|
||||||
|
}
|
||||||
|
SwarmSyncDisabled = cli.BoolFlag{
|
||||||
|
Name: "bzznosync",
|
||||||
|
Usage: "Swarm Syncing disabled (false)",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// MustMakeDataDir retrieves the currently requested data directory, terminating
|
// MustMakeDataDir retrieves the currently requested data directory, terminating
|
||||||
|
|
@ -664,53 +668,6 @@ func MakePasswordList(ctx *cli.Context) []string {
|
||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (common.Address, string) {
|
|
||||||
// Try to unlock the specified account a few times
|
|
||||||
account, err := MakeAddress(accman, address)
|
|
||||||
if err != nil {
|
|
||||||
Fatalf("unable to unlock account %v: %v", address, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for trials := 0; trials < 3; trials++ {
|
|
||||||
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
|
|
||||||
password := GetPassPhrase(prompt, false, i, passwords)
|
|
||||||
if err := accman.Unlock(account, password); err == nil {
|
|
||||||
return account, password
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// All trials expended to unlock account, bail out
|
|
||||||
Fatalf("Failed to unlock account: %s", address)
|
|
||||||
return common.Address{}, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPassPhrase retrieves the passwor associated with an account, either fetched
|
|
||||||
// from a list of preloaded passphrases, or requested interactively from the user.
|
|
||||||
func GetPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
|
|
||||||
// If a list of passwords was supplied, retrieve from them
|
|
||||||
if len(passwords) > 0 {
|
|
||||||
if i < len(passwords) {
|
|
||||||
return passwords[i]
|
|
||||||
}
|
|
||||||
return passwords[len(passwords)-1]
|
|
||||||
}
|
|
||||||
// Otherwise prompt the user for the password
|
|
||||||
fmt.Println(prompt)
|
|
||||||
password, err := PromptPassword("Passphrase: ", true)
|
|
||||||
if err != nil {
|
|
||||||
Fatalf("Failed to read passphrase: %v", err)
|
|
||||||
}
|
|
||||||
if confirmation {
|
|
||||||
confirm, err := PromptPassword("Repeat passphrase: ", false)
|
|
||||||
if err != nil {
|
|
||||||
Fatalf("Failed to read passphrase confirmation: %v", err)
|
|
||||||
}
|
|
||||||
if password != confirm {
|
|
||||||
Fatalf("Passphrases do not match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return password
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeSystemNode sets up a local node, configures the services to launch and
|
// MakeSystemNode sets up a local node, configures the services to launch and
|
||||||
// assembles the P2P protocol stack.
|
// assembles the P2P protocol stack.
|
||||||
func MakeSystemNode(name, version string, relconf release.Config, extra []byte, ctx *cli.Context) *node.Node {
|
func MakeSystemNode(name, version string, relconf release.Config, extra []byte, ctx *cli.Context) *node.Node {
|
||||||
|
|
@ -724,6 +681,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||||
if networks > 1 {
|
if networks > 1 {
|
||||||
Fatalf("The %v flags are mutually exclusive", netFlags)
|
Fatalf("The %v flags are mutually exclusive", netFlags)
|
||||||
}
|
}
|
||||||
|
// Configure the node's service container
|
||||||
datadir := MustMakeDataDir(ctx)
|
datadir := MustMakeDataDir(ctx)
|
||||||
netprv := MakeNodeKey(ctx)
|
netprv := MakeNodeKey(ctx)
|
||||||
// Configure the node's service container
|
// Configure the node's service container
|
||||||
|
|
@ -754,7 +712,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||||
accounts := strings.Split(ctx.GlobalString(UnlockedAccountFlag.Name), ",")
|
accounts := strings.Split(ctx.GlobalString(UnlockedAccountFlag.Name), ",")
|
||||||
for i, account := range accounts {
|
for i, account := range accounts {
|
||||||
if trimmed := strings.TrimSpace(account); trimmed != "" {
|
if trimmed := strings.TrimSpace(account); trimmed != "" {
|
||||||
UnlockAccount(ctx, accman, trimmed, i, passwords)
|
UnlockAccount(accman, trimmed, i, passwords)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -850,13 +808,12 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
Fatalf("Failed to register the account manager service: %v", err)
|
Fatalf("Failed to register the account manager service: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
return eth.New(ctx, ethConf)
|
return eth.New(ctx, ethConf)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
Fatalf("Failed to register the Ethereum service: %v", err)
|
Fatalf("Failed to register the Ethereum service: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whisper
|
|
||||||
if shhEnable {
|
if shhEnable {
|
||||||
if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
|
if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
|
||||||
Fatalf("Failed to register the Whisper service: %v", err)
|
Fatalf("Failed to register the Whisper service: %v", err)
|
||||||
|
|
@ -866,13 +823,14 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||||
return release.NewReleaseService(ctx, relconf)
|
return release.NewReleaseService(ctx, relconf)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
Fatalf("Failed to register the Geth release oracle service: %v", err)
|
Fatalf("Failed to register the Geth release oracle service: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// bzz. Swarm
|
// bzz. Swarm
|
||||||
var bzzconfig *bzzapi.Config
|
var bzzconfig *bzzapi.Config
|
||||||
hexaddr := ctx.GlobalString(SwarmAccountAddrFlag.Name)
|
hexaddr := ctx.GlobalString(SwarmAccountAddrFlag.Name)
|
||||||
if hexaddr != "" {
|
if hexaddr != "" {
|
||||||
swarmaccount := common.HexToAddress(hexaddr)
|
swarmaccount := common.HexToAddress(hexaddr)
|
||||||
if !accman.HasAccount(swarmaccount) {
|
if !accman.HasAddress(swarmaccount) {
|
||||||
Fatalf("swarm account '%v' does not exist: %v", hexaddr, err)
|
Fatalf("swarm account '%v' does not exist: %v", hexaddr, err)
|
||||||
}
|
}
|
||||||
prvkey, err := accman.GetUnlocked(swarmaccount)
|
prvkey, err := accman.GetUnlocked(swarmaccount)
|
||||||
|
|
@ -888,13 +846,19 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("unable to configure swarm: %v", err)
|
Fatalf("unable to configure swarm: %v", err)
|
||||||
}
|
}
|
||||||
swap := ctx.GlobalBool(SwarmSwapDisabled.Name)
|
bzzport := ctx.GlobalString(SwarmPortFlag.Name)
|
||||||
|
if len(bzzport) > 0 {
|
||||||
|
bzzconfig.Port = bzzport
|
||||||
|
}
|
||||||
|
swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name)
|
||||||
|
syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name)
|
||||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
return swarm.NewSwarm(ctx, bzzconfig, swap)
|
return swarm.NewSwarm(ctx, bzzconfig, swapEnabled, syncEnabled)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
Fatalf("Failed to register the Swarm service: %v", err)
|
Fatalf("Failed to register the Swarm service: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return stack
|
return stack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
128
cmd/utils/input.go
Normal file
128
cmd/utils/input.go
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/peterh/liner"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Holds the stdin line reader.
|
||||||
|
// Only this reader may be used for input because it keeps
|
||||||
|
// an internal buffer.
|
||||||
|
var Stdin = newUserInputReader()
|
||||||
|
|
||||||
|
type userInputReader struct {
|
||||||
|
*liner.State
|
||||||
|
warned bool
|
||||||
|
supported bool
|
||||||
|
normalMode liner.ModeApplier
|
||||||
|
rawMode liner.ModeApplier
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUserInputReader() *userInputReader {
|
||||||
|
r := new(userInputReader)
|
||||||
|
// Get the original mode before calling NewLiner.
|
||||||
|
// This is usually regular "cooked" mode where characters echo.
|
||||||
|
normalMode, _ := liner.TerminalMode()
|
||||||
|
// Turn on liner. It switches to raw mode.
|
||||||
|
r.State = liner.NewLiner()
|
||||||
|
rawMode, err := liner.TerminalMode()
|
||||||
|
if err != nil || !liner.TerminalSupported() {
|
||||||
|
r.supported = false
|
||||||
|
} else {
|
||||||
|
r.supported = true
|
||||||
|
r.normalMode = normalMode
|
||||||
|
r.rawMode = rawMode
|
||||||
|
// Switch back to normal mode while we're not prompting.
|
||||||
|
normalMode.ApplyMode()
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *userInputReader) Prompt(prompt string) (string, error) {
|
||||||
|
if r.supported {
|
||||||
|
r.rawMode.ApplyMode()
|
||||||
|
defer r.normalMode.ApplyMode()
|
||||||
|
} else {
|
||||||
|
// liner tries to be smart about printing the prompt
|
||||||
|
// and doesn't print anything if input is redirected.
|
||||||
|
// Un-smart it by printing the prompt always.
|
||||||
|
fmt.Print(prompt)
|
||||||
|
prompt = ""
|
||||||
|
defer fmt.Println()
|
||||||
|
}
|
||||||
|
return r.State.Prompt(prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *userInputReader) PasswordPrompt(prompt string) (passwd string, err error) {
|
||||||
|
if r.supported {
|
||||||
|
r.rawMode.ApplyMode()
|
||||||
|
defer r.normalMode.ApplyMode()
|
||||||
|
return r.State.PasswordPrompt(prompt)
|
||||||
|
}
|
||||||
|
if !r.warned {
|
||||||
|
fmt.Println("!! Unsupported terminal, password will be echoed.")
|
||||||
|
r.warned = true
|
||||||
|
}
|
||||||
|
// Just as in Prompt, handle printing the prompt here instead of relying on liner.
|
||||||
|
fmt.Print(prompt)
|
||||||
|
passwd, err = r.State.Prompt("")
|
||||||
|
fmt.Println()
|
||||||
|
return passwd, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *userInputReader) ConfirmPrompt(prompt string) (bool, error) {
|
||||||
|
prompt = prompt + " [y/N] "
|
||||||
|
input, err := r.Prompt(prompt)
|
||||||
|
if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPassPhrase retrieves the password associated with an account, either fetched
|
||||||
|
// from a list of preloaded passphrases, or requested interactively from the user.
|
||||||
|
func GetPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
|
||||||
|
// If a list of passwords was supplied, retrieve from them
|
||||||
|
if len(passwords) > 0 {
|
||||||
|
if i < len(passwords) {
|
||||||
|
return passwords[i]
|
||||||
|
}
|
||||||
|
return passwords[len(passwords)-1]
|
||||||
|
}
|
||||||
|
// Otherwise prompt the user for the password
|
||||||
|
if prompt != "" {
|
||||||
|
fmt.Println(prompt)
|
||||||
|
}
|
||||||
|
password, err := Stdin.PasswordPrompt("Passphrase: ")
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Failed to read passphrase: %v", err)
|
||||||
|
}
|
||||||
|
if confirmation {
|
||||||
|
confirm, err := Stdin.PasswordPrompt("Repeat passphrase: ")
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Failed to read passphrase confirmation: %v", err)
|
||||||
|
}
|
||||||
|
if password != confirm {
|
||||||
|
Fatalf("Passphrases do not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
package chequebook
|
|
||||||
|
|
||||||
import ()
|
|
||||||
|
|
||||||
const (
|
|
||||||
ContractCode = `606060405260008054600160a060020a03191633179055610201806100246000396000f3606060405260e060020a600035046341c0e1b58114610031578063d75d691d14610059578063fbf788d61461007e575b005b61002f60005433600160a060020a03908116911614156101ff57600054600160a060020a0316ff5b600160a060020a03600435166000908152600160205260409020546060908152602090f35b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100b8575b505050505050565b30600160a060020a039081166c0100000000000000000000000090810260609081529188160260745260888690526048812080825260ff8616608090815260a086905260c0859052909260019260e0926020928290866161da5a03f11561000257505060405151600054600160a060020a03908116911614610139576100b0565b85600160a060020a031660006001600050600089600160a060020a03168152602001908152602001600020600050548703604051809050600060405180830381858888f19350505050156101b357846001600050600088600160a060020a03168152602001908152602001600020600050819055506100b0565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff5b56`
|
|
||||||
|
|
||||||
ContractDeployedCode = `0x606060405260e060020a600035046341c0e1b58114610031578063d75d691d14610059578063fbf788d61461007e575b005b61002f60005433600160a060020a03908116911614156101ff57600054600160a060020a0316ff5b600160a060020a03600435166000908152600160205260409020546060908152602090f35b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100b8575b505050505050565b30600160a060020a039081166c0100000000000000000000000090810260609081529188160260745260888690526048812080825260ff8616608090815260a086905260c0859052909260019260e0926020928290866161da5a03f11561000257505060405151600054600160a060020a03908116911614610139576100b0565b85600160a060020a031660006001600050600089600160a060020a03168152602001908152602001600020600050548703604051809050600060405180830381858888f19350505050156101b357846001600050600088600160a060020a03168152602001908152602001600020600050819055506100b0565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff5b56`
|
|
||||||
|
|
||||||
ContractAbi = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"beneficiary","type":"address"}],"name":"getSent","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"amount","type":"uint256"},{"name":"sig_v","type":"uint8"},{"name":"sig_r","type":"bytes32"},{"name":"sig_s","type":"bytes32"}],"name":"cash","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"deadbeat","type":"address"}],"name":"Overdraft","type":"event"}]`
|
|
||||||
|
|
||||||
ContractSource = `
|
|
||||||
import "mortal";
|
|
||||||
|
|
||||||
/// @title Chequebook for Ethereum micropayments
|
|
||||||
/// @author Daniel A. Nagy <daniel@ethdev.com>
|
|
||||||
contract chequebook is mortal {
|
|
||||||
// Cumulative paid amount in wei to each beneficiary
|
|
||||||
mapping (address => uint256) sent;
|
|
||||||
|
|
||||||
/// @notice Overdraft event
|
|
||||||
event Overdraft(address deadbeat);
|
|
||||||
|
|
||||||
/// @notice Accessor for sent map
|
|
||||||
///
|
|
||||||
/// @param beneficiary beneficiary address
|
|
||||||
/// @return cumulative amount in wei sent to beneficiary
|
|
||||||
function getSent(address beneficiary) returns (uint256) {
|
|
||||||
return sent[beneficiary];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Cash cheque
|
|
||||||
///
|
|
||||||
/// @param beneficiary beneficiary address
|
|
||||||
/// @param amount cumulative amount in wei
|
|
||||||
/// @param sig_v signature parameter v
|
|
||||||
/// @param sig_r signature parameter r
|
|
||||||
/// @param sig_s signature parameter s
|
|
||||||
/// The digital signature is calculated on the concatenated triplet of contract address, beneficiary address and cumulative amount
|
|
||||||
function cash(address beneficiary, uint256 amount,
|
|
||||||
uint8 sig_v, bytes32 sig_r, bytes32 sig_s) {
|
|
||||||
// Check if the cheque is old.
|
|
||||||
// Only cheques that are more recent than the last cashed one are considered.
|
|
||||||
if(amount <= sent[beneficiary]) return;
|
|
||||||
// Check the digital signature of the cheque.
|
|
||||||
bytes32 hash = sha3(address(this), beneficiary, amount);
|
|
||||||
if(owner != ecrecover(hash, sig_v, sig_r, sig_s)) return;
|
|
||||||
// Attempt sending the difference between the cumulative amount on the cheque
|
|
||||||
// and the cumulative amount on the last cashed cheque to beneficiary.
|
|
||||||
if (beneficiary.send(amount - sent[beneficiary])) {
|
|
||||||
// Upon success, update the cumulative amount.
|
|
||||||
sent[beneficiary] = amount;
|
|
||||||
} else {
|
|
||||||
// Upon failure, punish owner for writing a bounced cheque.
|
|
||||||
// owner.sendToDebtorsPrison();
|
|
||||||
Overdraft(owner);
|
|
||||||
// Compensate beneficiary.
|
|
||||||
suicide(beneficiary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
)
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,279 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package ethreg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
|
||||||
"github.com/ethereum/go-ethereum/common/registrar"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// registryAPIBackend is a backend for an Ethereum Registry.
|
|
||||||
type registryAPIBackend struct {
|
|
||||||
config *core.ChainConfig
|
|
||||||
bc *core.BlockChain
|
|
||||||
chainDb ethdb.Database
|
|
||||||
txPool *core.TxPool
|
|
||||||
am *accounts.Manager
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrivateRegistarAPI offers various functions to access the Ethereum registry.
|
|
||||||
type PrivateRegistarAPI struct {
|
|
||||||
config *core.ChainConfig
|
|
||||||
be *registryAPIBackend
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance.
|
|
||||||
func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
|
|
||||||
return &PrivateRegistarAPI{
|
|
||||||
config: config,
|
|
||||||
be: ®istryAPIBackend{
|
|
||||||
config: config,
|
|
||||||
bc: bc,
|
|
||||||
chainDb: chainDb,
|
|
||||||
txPool: txPool,
|
|
||||||
am: am,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetGlobalRegistrar allows clients to set the global registry for the node.
|
|
||||||
// This method can be used to deploy a new registry. First zero out the current
|
|
||||||
// address by calling the method with namereg = '0x0' and then call this method
|
|
||||||
// again with '' as namereg. This will submit a transaction to the network which
|
|
||||||
// will deploy a new registry on execution. The TX hash is returned. When called
|
|
||||||
// with namereg '' and the current address is not zero the current global is
|
|
||||||
// address is returned..
|
|
||||||
func (api *PrivateRegistarAPI) SetGlobalRegistrar(namereg string, from common.Address) (string, error) {
|
|
||||||
return registrar.New(api.be).SetGlobalRegistrar(namereg, from)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHashReg queries the registry for a hash.
|
|
||||||
func (api *PrivateRegistarAPI) SetHashReg(hashreg string, from common.Address) (string, error) {
|
|
||||||
return registrar.New(api.be).SetHashReg(hashreg, from)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUrlHint queries the registry for an url.
|
|
||||||
func (api *PrivateRegistarAPI) SetUrlHint(hashreg string, from common.Address) (string, error) {
|
|
||||||
return registrar.New(api.be).SetUrlHint(hashreg, from)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveInfo stores contract information on the local file system.
|
|
||||||
func (api *PrivateRegistarAPI) SaveInfo(info *compiler.ContractInfo, filename string) (contenthash common.Hash, err error) {
|
|
||||||
return compiler.SaveInfo(info, filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register registers a new content hash in the registry.
|
|
||||||
func (api *PrivateRegistarAPI) Register(sender common.Address, addr common.Address, contentHashHex string) (bool, error) {
|
|
||||||
block := api.be.bc.CurrentBlock()
|
|
||||||
state, err := state.New(block.Root(), api.be.chainDb)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
codeb := state.GetCode(addr)
|
|
||||||
codeHash := common.BytesToHash(crypto.Keccak256(codeb))
|
|
||||||
contentHash := common.HexToHash(contentHashHex)
|
|
||||||
|
|
||||||
_, err = registrar.New(api.be).SetHashToHash(sender, codeHash, contentHash)
|
|
||||||
return err == nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterUrl registers a new url in the registry.
|
|
||||||
func (api *PrivateRegistarAPI) RegisterUrl(sender common.Address, contentHashHex string, url string) (bool, error) {
|
|
||||||
_, err := registrar.New(api.be).SetUrlToHash(sender, common.HexToHash(contentHashHex), url)
|
|
||||||
return err == nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// callmsg is the message type used for call transations.
|
|
||||||
type callmsg struct {
|
|
||||||
from *state.StateObject
|
|
||||||
to *common.Address
|
|
||||||
gas, gasPrice *big.Int
|
|
||||||
value *big.Int
|
|
||||||
data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// accessor boilerplate to implement core.Message
|
|
||||||
func (m callmsg) From() (common.Address, error) {
|
|
||||||
return m.from.Address(), nil
|
|
||||||
}
|
|
||||||
func (m callmsg) FromFrontier() (common.Address, error) {
|
|
||||||
return m.from.Address(), nil
|
|
||||||
}
|
|
||||||
func (m callmsg) Nonce() uint64 {
|
|
||||||
return m.from.Nonce()
|
|
||||||
}
|
|
||||||
func (m callmsg) To() *common.Address {
|
|
||||||
return m.to
|
|
||||||
}
|
|
||||||
func (m callmsg) GasPrice() *big.Int {
|
|
||||||
return m.gasPrice
|
|
||||||
}
|
|
||||||
func (m callmsg) Gas() *big.Int {
|
|
||||||
return m.gas
|
|
||||||
}
|
|
||||||
func (m callmsg) Value() *big.Int {
|
|
||||||
return m.value
|
|
||||||
}
|
|
||||||
func (m callmsg) Data() []byte {
|
|
||||||
return m.data
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call forms a transaction from the given arguments and tries to execute it on
|
|
||||||
// a private VM with a copy of the state. Any changes are therefore only temporary
|
|
||||||
// and not part of the actual state. This allows for local execution/queries.
|
|
||||||
func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
|
|
||||||
block := be.bc.CurrentBlock()
|
|
||||||
statedb, err := state.New(block.Root(), be.chainDb)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var from *state.StateObject
|
|
||||||
if len(fromStr) == 0 {
|
|
||||||
accounts := be.am.Accounts()
|
|
||||||
if len(accounts) == 0 {
|
|
||||||
from = statedb.GetOrNewStateObject(common.Address{})
|
|
||||||
} else {
|
|
||||||
from = statedb.GetOrNewStateObject(accounts[0].Address)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr))
|
|
||||||
}
|
|
||||||
|
|
||||||
from.SetBalance(common.MaxBig)
|
|
||||||
|
|
||||||
msg := callmsg{
|
|
||||||
from: from,
|
|
||||||
gas: common.Big(gasStr),
|
|
||||||
gasPrice: common.Big(gasPriceStr),
|
|
||||||
value: common.Big(valueStr),
|
|
||||||
data: common.FromHex(dataStr),
|
|
||||||
}
|
|
||||||
if len(toStr) > 0 {
|
|
||||||
addr := common.HexToAddress(toStr)
|
|
||||||
msg.to = &addr
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.gas.Cmp(big.NewInt(0)) == 0 {
|
|
||||||
msg.gas = big.NewInt(50000000)
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.gasPrice.Cmp(big.NewInt(0)) == 0 {
|
|
||||||
msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
|
|
||||||
}
|
|
||||||
|
|
||||||
header := be.bc.CurrentBlock().Header()
|
|
||||||
vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{})
|
|
||||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
|
||||||
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
|
|
||||||
|
|
||||||
return common.ToHex(res), gas.String(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// StorageAt returns the data stores in the state for the given address and location.
|
|
||||||
func (be *registryAPIBackend) StorageAt(addr string, storageAddr string) string {
|
|
||||||
block := be.bc.CurrentBlock()
|
|
||||||
state, err := state.New(block.Root(), be.chainDb)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transact forms a transaction from the given arguments and submits it to the
|
|
||||||
// transactio pool for execution.
|
|
||||||
func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
|
||||||
if len(toStr) > 0 && toStr != "0x" && !common.IsHexAddress(toStr) {
|
|
||||||
return "", errors.New("invalid address")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
from = common.HexToAddress(fromStr)
|
|
||||||
to = common.HexToAddress(toStr)
|
|
||||||
value = common.Big(valueStr)
|
|
||||||
gas *big.Int
|
|
||||||
price *big.Int
|
|
||||||
data []byte
|
|
||||||
contractCreation bool
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(gasStr) == 0 {
|
|
||||||
gas = big.NewInt(90000)
|
|
||||||
} else {
|
|
||||||
gas = common.Big(gasStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(gasPriceStr) == 0 {
|
|
||||||
price = big.NewInt(10000000000000)
|
|
||||||
} else {
|
|
||||||
price = common.Big(gasPriceStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
data = common.FromHex(codeStr)
|
|
||||||
if len(toStr) == 0 {
|
|
||||||
contractCreation = true
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce := be.txPool.State().GetNonce(from)
|
|
||||||
if len(nonceStr) != 0 {
|
|
||||||
nonce = common.Big(nonceStr).Uint64()
|
|
||||||
}
|
|
||||||
|
|
||||||
var tx *types.Transaction
|
|
||||||
if contractCreation {
|
|
||||||
tx = types.NewContractCreation(nonce, value, gas, price, data)
|
|
||||||
} else {
|
|
||||||
tx = types.NewTransaction(nonce, to, value, gas, price, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
signature, err := be.am.Sign(from, tx.SigHash().Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
signedTx, err := tx.WithSignature(signature)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
be.txPool.SetLocal(signedTx)
|
|
||||||
if err := be.txPool.Add(signedTx); err != nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if contractCreation {
|
|
||||||
addr := crypto.CreateAddress(from, nonce)
|
|
||||||
glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex())
|
|
||||||
} else {
|
|
||||||
glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
return signedTx.Hash().Hex(), nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,436 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package registrar
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Registrar implements the Ethereum name registrar services mapping
|
|
||||||
- arbitrary strings to ethereum addresses
|
|
||||||
- hashes to hashes
|
|
||||||
- hashes to arbitrary strings
|
|
||||||
(likely will provide lookup service for all three)
|
|
||||||
|
|
||||||
The Registrar is used by
|
|
||||||
* the roundtripper transport implementation of
|
|
||||||
url schemes to resolve domain names and services that register these names
|
|
||||||
* contract info retrieval (NatSpec).
|
|
||||||
|
|
||||||
The Registrar uses 3 contracts on the blockchain:
|
|
||||||
* GlobalRegistrar: Name (string) -> Address (Owner)
|
|
||||||
* HashReg : Key Hash (hash of domain name or contract code) -> Content Hash
|
|
||||||
* UrlHint : Content Hash -> Url Hint
|
|
||||||
|
|
||||||
These contracts are (currently) not included in the genesis block.
|
|
||||||
Each Set<X> needs to be called once on each blockchain/network once.
|
|
||||||
|
|
||||||
Contract addresses need to be set the first time any Registrar method is called
|
|
||||||
in a client session.
|
|
||||||
This is done for frontier by default, otherwise the caller needs to make sure
|
|
||||||
the relevant environment initialised the desired contracts
|
|
||||||
*/
|
|
||||||
var (
|
|
||||||
// GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" // olympic
|
|
||||||
GlobalRegistrarAddr = "0x33990122638b9132ca29c723bdf037f1a891a70c" // frontier
|
|
||||||
HashRegAddr = "0x23bf622b5a65f6060d855fca401133ded3520620" // frontier
|
|
||||||
UrlHintAddr = "0x73ed5ef6c010727dfd2671dbb70faac19ec18626" // frontier
|
|
||||||
|
|
||||||
zero = regexp.MustCompile("^(0x)?0*$")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
trueHex = "0000000000000000000000000000000000000000000000000000000000000001"
|
|
||||||
falseHex = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
)
|
|
||||||
|
|
||||||
func abiSignature(s string) string {
|
|
||||||
return common.ToHex(crypto.Keccak256([]byte(s))[:4])
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
HashRegName = "HashReg"
|
|
||||||
UrlHintName = "UrlHint"
|
|
||||||
|
|
||||||
registerContentHashAbi = abiSignature("register(uint256,uint256)")
|
|
||||||
registerUrlAbi = abiSignature("register(uint256,uint8,uint256)")
|
|
||||||
setOwnerAbi = abiSignature("setowner()")
|
|
||||||
reserveAbi = abiSignature("reserve(bytes32)")
|
|
||||||
resolveAbi = abiSignature("addr(bytes32)")
|
|
||||||
registerAbi = abiSignature("setAddress(bytes32,address,bool)")
|
|
||||||
addressAbiPrefix = falseHex[:24]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Registrar's backend is defined as an interface (implemented by xeth, but could be remote)
|
|
||||||
type Backend interface {
|
|
||||||
StorageAt(string, string) string
|
|
||||||
Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
|
|
||||||
Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Registrar should also just implement The Resolver and Registry interfaces
|
|
||||||
// Simplify for now.
|
|
||||||
type VersionedRegistrar interface {
|
|
||||||
Resolver(*big.Int) *Registrar
|
|
||||||
Registry() *Registrar
|
|
||||||
}
|
|
||||||
|
|
||||||
type Registrar struct {
|
|
||||||
backend Backend
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(b Backend) (res *Registrar) {
|
|
||||||
res = &Registrar{b}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (txhash string, err error) {
|
|
||||||
if namereg != "" {
|
|
||||||
GlobalRegistrarAddr = namereg
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if zero.MatchString(GlobalRegistrarAddr) {
|
|
||||||
if (addr == common.Address{}) {
|
|
||||||
err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given")
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (txhash string, err error) {
|
|
||||||
if hashreg != "" {
|
|
||||||
HashRegAddr = hashreg
|
|
||||||
} else {
|
|
||||||
if !zero.MatchString(HashRegAddr) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nameHex, extra := encodeName(HashRegName, 2)
|
|
||||||
hashRegAbi := resolveAbi + nameHex + extra
|
|
||||||
glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi)
|
|
||||||
var res string
|
|
||||||
res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
|
|
||||||
if len(res) >= 40 {
|
|
||||||
HashRegAddr = "0x" + res[len(res)-40:len(res)]
|
|
||||||
}
|
|
||||||
if err != nil || zero.MatchString(HashRegAddr) {
|
|
||||||
if (addr == common.Address{}) {
|
|
||||||
err = fmt.Errorf("HashReg address not found and sender for creation not given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", HashRegCode)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err)
|
|
||||||
}
|
|
||||||
glog.V(logger.Detail).Infof("created HashRegAddr @ txhash %v\n", txhash)
|
|
||||||
} else {
|
|
||||||
glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, err error) {
|
|
||||||
if urlhint != "" {
|
|
||||||
UrlHintAddr = urlhint
|
|
||||||
} else {
|
|
||||||
if !zero.MatchString(UrlHintAddr) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nameHex, extra := encodeName(UrlHintName, 2)
|
|
||||||
urlHintAbi := resolveAbi + nameHex + extra
|
|
||||||
glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr)
|
|
||||||
var res string
|
|
||||||
res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
|
|
||||||
if len(res) >= 40 {
|
|
||||||
UrlHintAddr = "0x" + res[len(res)-40:len(res)]
|
|
||||||
}
|
|
||||||
if err != nil || zero.MatchString(UrlHintAddr) {
|
|
||||||
if (addr == common.Address{}) {
|
|
||||||
err = fmt.Errorf("UrlHint address not found and sender for creation not given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err)
|
|
||||||
}
|
|
||||||
glog.V(logger.Detail).Infof("created UrlHint @ txhash %v\n", txhash)
|
|
||||||
} else {
|
|
||||||
glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReserveName(from, name) reserves name for the sender address in the globalRegistrar
|
|
||||||
// the tx needs to be mined to take effect
|
|
||||||
func (self *Registrar) ReserveName(address common.Address, name string) (txh string, err error) {
|
|
||||||
if zero.MatchString(GlobalRegistrarAddr) {
|
|
||||||
return "", fmt.Errorf("GlobalRegistrar address is not set")
|
|
||||||
}
|
|
||||||
nameHex, extra := encodeName(name, 2)
|
|
||||||
abi := reserveAbi + nameHex + extra
|
|
||||||
glog.V(logger.Detail).Infof("Reserve data: %s", abi)
|
|
||||||
return self.backend.Transact(
|
|
||||||
address.Hex(),
|
|
||||||
GlobalRegistrarAddr,
|
|
||||||
"", "", "", "",
|
|
||||||
abi,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAddressToName(from, name, addr) will set the Address to address for name
|
|
||||||
// in the globalRegistrar using from as the sender of the transaction
|
|
||||||
// the tx needs to be mined to take effect
|
|
||||||
func (self *Registrar) SetAddressToName(from common.Address, name string, address common.Address) (txh string, err error) {
|
|
||||||
if zero.MatchString(GlobalRegistrarAddr) {
|
|
||||||
return "", fmt.Errorf("GlobalRegistrar address is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
nameHex, extra := encodeName(name, 6)
|
|
||||||
addrHex := encodeAddress(address)
|
|
||||||
|
|
||||||
abi := registerAbi + nameHex + addrHex + trueHex + extra
|
|
||||||
glog.V(logger.Detail).Infof("SetAddressToName data: %s to %s ", abi, GlobalRegistrarAddr)
|
|
||||||
|
|
||||||
return self.backend.Transact(
|
|
||||||
from.Hex(),
|
|
||||||
GlobalRegistrarAddr,
|
|
||||||
"", "", "", "",
|
|
||||||
abi,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NameToAddr(from, name) queries the registrar for the address on name
|
|
||||||
func (self *Registrar) NameToAddr(from common.Address, name string) (address common.Address, err error) {
|
|
||||||
if zero.MatchString(GlobalRegistrarAddr) {
|
|
||||||
return address, fmt.Errorf("GlobalRegistrar address is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
nameHex, extra := encodeName(name, 2)
|
|
||||||
abi := resolveAbi + nameHex + extra
|
|
||||||
glog.V(logger.Detail).Infof("NameToAddr data: %s", abi)
|
|
||||||
res, _, err := self.backend.Call(
|
|
||||||
from.Hex(),
|
|
||||||
GlobalRegistrarAddr,
|
|
||||||
"", "", "",
|
|
||||||
abi,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
address = common.HexToAddress(res)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// called as first step in the registration process on HashReg
|
|
||||||
func (self *Registrar) SetOwner(address common.Address) (txh string, err error) {
|
|
||||||
if zero.MatchString(HashRegAddr) {
|
|
||||||
return "", fmt.Errorf("HashReg address is not set")
|
|
||||||
}
|
|
||||||
return self.backend.Transact(
|
|
||||||
address.Hex(),
|
|
||||||
HashRegAddr,
|
|
||||||
"", "", "", "",
|
|
||||||
setOwnerAbi,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// registers some content hash to a key/code hash
|
|
||||||
// e.g., the contract Info combined Json Doc's ContentHash
|
|
||||||
// to CodeHash of a contract or hash of a domain
|
|
||||||
func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) {
|
|
||||||
if zero.MatchString(HashRegAddr) {
|
|
||||||
return "", fmt.Errorf("HashReg address is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = self.SetOwner(address)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
codehex := common.Bytes2Hex(codehash[:])
|
|
||||||
dochex := common.Bytes2Hex(dochash[:])
|
|
||||||
|
|
||||||
data := registerContentHashAbi + codehex + dochex
|
|
||||||
glog.V(logger.Detail).Infof("SetHashToHash data: %s sent to %v\n", data, HashRegAddr)
|
|
||||||
return self.backend.Transact(
|
|
||||||
address.Hex(),
|
|
||||||
HashRegAddr,
|
|
||||||
"", "", "", "",
|
|
||||||
data,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched
|
|
||||||
// address is used as sender for the transaction and will be the owner of a new
|
|
||||||
// registry entry on first time use
|
|
||||||
// FIXME: silently doing nothing if sender is not the owner
|
|
||||||
// note that with content addressed storage, this step is no longer necessary
|
|
||||||
func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) {
|
|
||||||
if zero.MatchString(UrlHintAddr) {
|
|
||||||
return "", fmt.Errorf("UrlHint address is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
hashHex := common.Bytes2Hex(hash[:])
|
|
||||||
var urlHex string
|
|
||||||
urlb := []byte(url)
|
|
||||||
var cnt byte
|
|
||||||
n := len(urlb)
|
|
||||||
|
|
||||||
for n > 0 {
|
|
||||||
if n > 32 {
|
|
||||||
n = 32
|
|
||||||
}
|
|
||||||
urlHex = common.Bytes2Hex(urlb[:n])
|
|
||||||
urlb = urlb[n:]
|
|
||||||
n = len(urlb)
|
|
||||||
bcnt := make([]byte, 32)
|
|
||||||
bcnt[31] = cnt
|
|
||||||
data := registerUrlAbi +
|
|
||||||
hashHex +
|
|
||||||
common.Bytes2Hex(bcnt) +
|
|
||||||
common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32))
|
|
||||||
txh, err = self.backend.Transact(
|
|
||||||
address.Hex(),
|
|
||||||
UrlHintAddr,
|
|
||||||
"", "", "", "",
|
|
||||||
data,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cnt++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashToHash(key) resolves contenthash for key (a hash) using HashReg
|
|
||||||
// resolution is costless non-transactional
|
|
||||||
// implemented as direct retrieval from db
|
|
||||||
func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) {
|
|
||||||
if zero.MatchString(HashRegAddr) {
|
|
||||||
return common.Hash{}, fmt.Errorf("HashReg address is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
// look up in hashReg
|
|
||||||
at := HashRegAddr[2:]
|
|
||||||
key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
|
|
||||||
hash := self.backend.StorageAt(at, key)
|
|
||||||
|
|
||||||
if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) {
|
|
||||||
err = fmt.Errorf("HashToHash: content hash not found for '%v'", khash.Hex())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashToUrl(contenthash) resolves the url for contenthash using UrlHint
|
|
||||||
// resolution is costless non-transactional
|
|
||||||
// implemented as direct retrieval from db
|
|
||||||
// if we use content addressed storage, this step is no longer necessary
|
|
||||||
func (self *Registrar) HashToUrl(chash common.Hash) (uri string, err error) {
|
|
||||||
if zero.MatchString(UrlHintAddr) {
|
|
||||||
return "", fmt.Errorf("UrlHint address is not set")
|
|
||||||
}
|
|
||||||
// look up in URL reg
|
|
||||||
var str string = " "
|
|
||||||
var idx uint32
|
|
||||||
for len(str) > 0 {
|
|
||||||
mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
|
|
||||||
key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx)))
|
|
||||||
hex := self.backend.StorageAt(UrlHintAddr[2:], key)
|
|
||||||
str = string(common.Hex2Bytes(hex[2:]))
|
|
||||||
l := 0
|
|
||||||
for (l < len(str)) && (str[l] == 0) {
|
|
||||||
l++
|
|
||||||
}
|
|
||||||
|
|
||||||
str = str[l:]
|
|
||||||
uri = uri + str
|
|
||||||
idx++
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(uri) == 0 {
|
|
||||||
err = fmt.Errorf("HashToUrl: URL hint not found for '%v'", chash.Hex())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func storageIdx2Addr(varidx uint32) []byte {
|
|
||||||
data := make([]byte, 32)
|
|
||||||
binary.BigEndian.PutUint32(data[28:32], varidx)
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
func storageMapping(addr, key []byte) []byte {
|
|
||||||
data := make([]byte, 64)
|
|
||||||
copy(data[0:32], key[0:32])
|
|
||||||
copy(data[32:64], addr[0:32])
|
|
||||||
sha := crypto.Keccak256(data)
|
|
||||||
return sha
|
|
||||||
}
|
|
||||||
|
|
||||||
func storageFixedArray(addr, idx []byte) []byte {
|
|
||||||
var carry byte
|
|
||||||
for i := 31; i >= 0; i-- {
|
|
||||||
var b byte = addr[i] + idx[i] + carry
|
|
||||||
if b < addr[i] {
|
|
||||||
carry = 1
|
|
||||||
} else {
|
|
||||||
carry = 0
|
|
||||||
}
|
|
||||||
addr[i] = b
|
|
||||||
}
|
|
||||||
return addr
|
|
||||||
}
|
|
||||||
|
|
||||||
func storageAddress(addr []byte) string {
|
|
||||||
return common.ToHex(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeAddress(address common.Address) string {
|
|
||||||
return addressAbiPrefix + address.Hex()[2:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeName(name string, index uint8) (string, string) {
|
|
||||||
extra := common.Bytes2Hex([]byte(name))
|
|
||||||
if len(name) > 32 {
|
|
||||||
return fmt.Sprintf("%064x", index), extra
|
|
||||||
}
|
|
||||||
return extra + falseHex[len(extra):], ""
|
|
||||||
}
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package registrar
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testBackend struct {
|
|
||||||
// contracts mock
|
|
||||||
contracts map[string](map[string]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
text = "test"
|
|
||||||
codehash = common.StringToHash("1234")
|
|
||||||
hash = common.BytesToHash(crypto.Keccak256([]byte(text)))
|
|
||||||
url = "bzz://bzzhash/my/path/contr.act"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewTestBackend() *testBackend {
|
|
||||||
self := &testBackend{}
|
|
||||||
self.contracts = make(map[string](map[string]string))
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *testBackend) initHashReg() {
|
|
||||||
self.contracts[HashRegAddr[2:]] = make(map[string]string)
|
|
||||||
key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
|
|
||||||
self.contracts[HashRegAddr[2:]][key] = hash.Hex()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *testBackend) initUrlHint() {
|
|
||||||
self.contracts[UrlHintAddr[2:]] = make(map[string]string)
|
|
||||||
mapaddr := storageMapping(storageIdx2Addr(1), hash[:])
|
|
||||||
|
|
||||||
key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0)))
|
|
||||||
self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url))
|
|
||||||
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
|
|
||||||
self.contracts[UrlHintAddr[2:]][key] = "0x0"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *testBackend) StorageAt(ca, sa string) (res string) {
|
|
||||||
c := self.contracts[ca]
|
|
||||||
if c == nil {
|
|
||||||
return "0x0"
|
|
||||||
}
|
|
||||||
res = c[sa]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) {
|
|
||||||
return "", "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetGlobalRegistrar(t *testing.T) {
|
|
||||||
b := NewTestBackend()
|
|
||||||
res := New(b)
|
|
||||||
_, err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v'", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHashToHash(t *testing.T) {
|
|
||||||
b := NewTestBackend()
|
|
||||||
res := New(b)
|
|
||||||
|
|
||||||
HashRegAddr = "0x0"
|
|
||||||
got, err := res.HashToHash(codehash)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error")
|
|
||||||
} else {
|
|
||||||
exp := "HashReg address is not set"
|
|
||||||
if err.Error() != exp {
|
|
||||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HashRegAddr = common.BigToAddress(common.Big1).Hex() //[2:]
|
|
||||||
got, err = res.HashToHash(codehash)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error")
|
|
||||||
} else {
|
|
||||||
exp := "HashToHash: content hash not found for '" + codehash.Hex() + "'"
|
|
||||||
if err.Error() != exp {
|
|
||||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.initHashReg()
|
|
||||||
got, err = res.HashToHash(codehash)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected no error, got %v", err)
|
|
||||||
} else {
|
|
||||||
if got != hash {
|
|
||||||
t.Errorf("incorrect result, expected '%v', got '%v'", hash.Hex(), got.Hex())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHashToUrl(t *testing.T) {
|
|
||||||
b := NewTestBackend()
|
|
||||||
res := New(b)
|
|
||||||
|
|
||||||
UrlHintAddr = "0x0"
|
|
||||||
got, err := res.HashToUrl(hash)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error")
|
|
||||||
} else {
|
|
||||||
exp := "UrlHint address is not set"
|
|
||||||
if err.Error() != exp {
|
|
||||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UrlHintAddr = common.BigToAddress(common.Big2).Hex() //[2:]
|
|
||||||
got, err = res.HashToUrl(hash)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error")
|
|
||||||
} else {
|
|
||||||
exp := "HashToUrl: URL hint not found for '" + hash.Hex() + "'"
|
|
||||||
if err.Error() != exp {
|
|
||||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.initUrlHint()
|
|
||||||
got, err = res.HashToUrl(hash)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected no error, got %v", err)
|
|
||||||
} else {
|
|
||||||
if got != url {
|
|
||||||
t.Errorf("incorrect result, expected '%v', got '%s'", url, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -196,9 +196,15 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
|
||||||
if len(hash) != 32 {
|
if len(hash) != 32 {
|
||||||
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
|
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
|
||||||
}
|
}
|
||||||
|
if prv.D == nil {
|
||||||
|
panic("prv.D is nil")
|
||||||
|
}
|
||||||
|
if prv.Params() == nil {
|
||||||
|
panic("prv.Params() is nil")
|
||||||
|
}
|
||||||
|
|
||||||
seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8)
|
seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8)
|
||||||
defer zeroBytes(seckey)
|
// defer zeroBytes(seckey)
|
||||||
sig, err = secp256k1.Sign(hash, seckey)
|
sig, err = secp256k1.Sign(hash, seckey)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ func TestSign(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Sign error: %s", err)
|
t.Errorf("Sign error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
recoveredPub, err := Ecrecover(msg, sig)
|
recoveredPub, err := Ecrecover(msg, sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("ECRecover error: %s", err)
|
t.Errorf("ECRecover error: %s", err)
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/registrar"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReadBits(t *testing.T) {
|
func TestReadBits(t *testing.T) {
|
||||||
|
|
@ -39,34 +37,3 @@ func TestReadBits(t *testing.T) {
|
||||||
check("0000000000012345000000000000000000000000000000000000FEFCF3F8F0")
|
check("0000000000012345000000000000000000000000000000000000FEFCF3F8F0")
|
||||||
check("18F8F8F1000111000110011100222004330052300000000000000000FEFCF3F8F0")
|
check("18F8F8F1000111000110011100222004330052300000000000000000FEFCF3F8F0")
|
||||||
}
|
}
|
||||||
|
|
||||||
type Backend interface {
|
|
||||||
registrar.Backend
|
|
||||||
AtStateNum(int64) registrar.Backend
|
|
||||||
}
|
|
||||||
|
|
||||||
// implements a versioned Registrar on an archiving full node
|
|
||||||
type EthReg struct {
|
|
||||||
backend Backend
|
|
||||||
registry *registrar.Registrar
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(backend Backend) (self *EthReg) {
|
|
||||||
self = &EthReg{backend: backend}
|
|
||||||
self.registry = registrar.New(backend)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *EthReg) Registry() *registrar.Registrar {
|
|
||||||
return self.registry
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *EthReg) Resolver(n *big.Int) *registrar.Registrar {
|
|
||||||
var s registrar.Backend
|
|
||||||
if n != nil {
|
|
||||||
s = self.backend.AtStateNum(n.Int64())
|
|
||||||
} else {
|
|
||||||
s = registrar.Backend(self.backend)
|
|
||||||
}
|
|
||||||
return registrar.New(s)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
"github.com/ethereum/go-ethereum/common/compiler"
|
||||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||||
"github.com/ethereum/go-ethereum/common/registrar/ethreg"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -342,16 +341,12 @@ func (s *Ethereum) APIs() []rpc.API {
|
||||||
}, {
|
}, {
|
||||||
Namespace: "debug",
|
Namespace: "debug",
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: NewPrivateDebugAPI(s.chainConfig, s),
|
Service: NewPrivateDebugAPI(s.ChainConfig(), s),
|
||||||
}, {
|
}, {
|
||||||
Namespace: "net",
|
Namespace: "net",
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: s.netRPCService,
|
Service: s.netRPCService,
|
||||||
Public: true,
|
Public: true,
|
||||||
}, {
|
|
||||||
Namespace: "admin",
|
|
||||||
Version: "1.0",
|
|
||||||
Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager),
|
|
||||||
},
|
},
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
|
|
@ -384,6 +379,8 @@ func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
||||||
|
|
||||||
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
||||||
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
||||||
|
func (s *Ethereum) GPO() *GasPriceOracle { return s.gpo }
|
||||||
|
func (s *Ethereum) ChainConfig() *core.ChainConfig { return s.chainConfig }
|
||||||
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
||||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||||
func (s *Ethereum) Pow() *ethash.Ethash { return s.pow }
|
func (s *Ethereum) Pow() *ethash.Ethash { return s.pow }
|
||||||
|
|
|
||||||
23
eth/bind.go
23
eth/bind.go
|
|
@ -20,6 +20,8 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
|
@ -68,7 +70,7 @@ func (b *ContractBackend) HasCode(ctx context.Context, contract common.Address,
|
||||||
// call with the specified data as the input. The pending flag requests execution
|
// call with the specified data as the input. The pending flag requests execution
|
||||||
// against the pending block, not the stable head of the chain.
|
// against the pending block, not the stable head of the chain.
|
||||||
func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
|
func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
|
||||||
if ctx == nil {
|
if ctx ==-- nil {
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
}
|
}
|
||||||
// Convert the input args to the API spec
|
// Convert the input args to the API spec
|
||||||
|
|
@ -80,7 +82,7 @@ func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Addr
|
||||||
if pending {
|
if pending {
|
||||||
block = rpc.PendingBlockNumber
|
block = rpc.PendingBlockNumber
|
||||||
}
|
}
|
||||||
// Execute the call and convert the output back to Go types
|
// Execute the call and convert the output ba--ck to Go types
|
||||||
out, err := b.bcapi.Call(ctx, args, block)
|
out, err := b.bcapi.Call(ctx, args, block)
|
||||||
return common.FromHex(out), err
|
return common.FromHex(out), err
|
||||||
}
|
}
|
||||||
|
|
@ -132,3 +134,20 @@ func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transac
|
||||||
_, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
|
_, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *ContractBackend) GetTxReceipt(txhash common.Hash) *types.Receipt {
|
||||||
|
return core.GetReceipt(b.eth.ChainDb(), txhash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ContractBackend) BalanceAt(address common.Address) *big.Int {
|
||||||
|
currentState, err := state.New(b.eth.BlockChain().CurrentBlock().Root(), b.eth.ChainDb())
|
||||||
|
if err != nil {
|
||||||
|
return new(big.Int)
|
||||||
|
}
|
||||||
|
return currentState.GetBalance(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ContractBackend) CodeAt(address common.Address) string {
|
||||||
|
currentState, _ := state.New(b.eth.BlockChain().CurrentBlock().Root(), b.eth.ChainDb())
|
||||||
|
return common.ToHex(currentState.GetCode(address))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,26 +23,4 @@ const (
|
||||||
FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks
|
FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks
|
||||||
FastSync // Quickly download the headers, full sync only at the chain head
|
FastSync // Quickly download the headers, full sync only at the chain head
|
||||||
LightSync // Download only the headers and terminate afterwards
|
LightSync // Download only the headers and terminate afterwards
|
||||||
AdminApiName = "admin"
|
|
||||||
BzzApiName = "bzz"
|
|
||||||
EthApiName = "eth"
|
|
||||||
DbApiName = "db"
|
|
||||||
DebugApiName = "debug"
|
|
||||||
MergedApiName = "merged"
|
|
||||||
MinerApiName = "miner"
|
|
||||||
NetApiName = "net"
|
|
||||||
ShhApiName = "shh"
|
|
||||||
TxPoolApiName = "txpool"
|
|
||||||
PersonalApiName = "personal"
|
|
||||||
Web3ApiName = "web3"
|
|
||||||
|
|
||||||
JsonRpcVersion = "2.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// All API's
|
|
||||||
AllApis = strings.Join([]string{
|
|
||||||
AdminApiName, BzzApiName, DbApiName, EthApiName, DebugApiName, MinerApiName, NetApiName,
|
|
||||||
ShhApiName, TxPoolApiName, PersonalApiName, Web3ApiName,
|
|
||||||
}, ",")
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,209 @@
|
||||||
package web3ext
|
package web3ext
|
||||||
|
|
||||||
var Modules = map[string]string{
|
var Modules = map[string]string{
|
||||||
"admin": Admin_JS,
|
"admin": Admin_JS,
|
||||||
"debug": Debug_JS,
|
"debug": Debug_JS,
|
||||||
"eth": Eth_JS,
|
"eth": Eth_JS,
|
||||||
"miner": Miner_JS,
|
"miner": Miner_JS,
|
||||||
"net": Net_JS,
|
"net": Net_JS,
|
||||||
"personal": Personal_JS,
|
"personal": Personal_JS,
|
||||||
"rpc": RPC_JS,
|
"rpc": RPC_JS,
|
||||||
"shh": Shh_JS,
|
"shh": Shh_JS,
|
||||||
"txpool": TxPool_JS,
|
"txpool": TxPool_JS,
|
||||||
|
"net": Net_JS,
|
||||||
|
"bzz": Bzz_JS,
|
||||||
|
"ens": ENS_JS,
|
||||||
|
"chequebook": Chequebook_JS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Bzz_JS = `
|
||||||
|
web3._extend({
|
||||||
|
property: 'bzz',
|
||||||
|
methods:
|
||||||
|
[
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'blockNetworkRead',
|
||||||
|
call: 'bzz_blockNetworkRead',
|
||||||
|
params: 1,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'syncEnabled',
|
||||||
|
call: 'bzz_syncEnabled',
|
||||||
|
params: 1,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'swapEnabled',
|
||||||
|
call: 'bzz_swapEnabled',
|
||||||
|
params: 1,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'download',
|
||||||
|
call: 'bzz_download',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null, null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'upload',
|
||||||
|
call: 'bzz_upload',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null, null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'retrieve',
|
||||||
|
call: 'bzz_retrieve',
|
||||||
|
params: 1,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'store',
|
||||||
|
call: 'bzz_store',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'get',
|
||||||
|
call: 'bzz_get',
|
||||||
|
params: 1,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'put',
|
||||||
|
call: 'bzz_put',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null, null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'modify',
|
||||||
|
call: 'bzz_modify',
|
||||||
|
params: 4,
|
||||||
|
inputFormatter: [null, null, null, null]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
properties:
|
||||||
|
[
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'hive',
|
||||||
|
getter: 'bzz_hive'
|
||||||
|
}),
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'info',
|
||||||
|
getter: 'bzz_info',
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
|
|
||||||
|
const ENS_JS = `
|
||||||
|
web3._extend({
|
||||||
|
property: 'ens',
|
||||||
|
methods:
|
||||||
|
[ new web3._extend.Method({
|
||||||
|
name: 'register',
|
||||||
|
call: 'ens_register',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null, null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method(
|
||||||
|
{
|
||||||
|
name: 'resolve',
|
||||||
|
call: 'ens_resolve',
|
||||||
|
params: 1,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
`
|
||||||
|
|
||||||
|
const Chequebook_JS = `
|
||||||
|
web3._extend({
|
||||||
|
property: 'chequebook',
|
||||||
|
methods:
|
||||||
|
[
|
||||||
|
new web3._extend.Method(
|
||||||
|
{
|
||||||
|
name: 'deposit',
|
||||||
|
call: 'chequebook_deposit',
|
||||||
|
}
|
||||||
|
out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber)
|
||||||
|
return out.Uint64(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuggestGasPrice implements bind.ContractTransactor retrieving the currently
|
||||||
|
// suggested gas price to allow a timely execution of a transaction.
|
||||||
|
func (b *ContractBackend
|
||||||
|
params: 1,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Propert
|
||||||
|
y({
|
||||||
|
name: 'balance',
|
||||||
|
getter: 'chequebook_balance',
|
||||||
|
outputFormatter: web3._extend.utils.toDecimal
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'cash',
|
||||||
|
call: 'chequebook_cash',
|
||||||
|
params: 1,
|
||||||
|
inputFormatter: [null]
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'issue',
|
||||||
|
|
||||||
|
call: 'chequebook_issue',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null, null]
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
|
|
||||||
|
const Personal_JS = `
|
||||||
|
|
||||||
|
web3._extend({
|
||||||
|
property: 'personal',
|
||||||
|
methods:
|
||||||
|
[
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'importRawKey',
|
||||||
|
call: 'personal_importRawKey',
|
||||||
|
params: 2
|
||||||
|
})
|
||||||
|
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
|
|
||||||
|
const TxPool_JS = `web3._extend({
|
||||||
|
property: 'txpool',
|
||||||
|
methods:
|
||||||
|
[
|
||||||
|
],
|
||||||
|
properties:
|
||||||
|
[
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'content',
|
||||||
|
getter: 'txpool_content'
|
||||||
|
}),
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'inspect',
|
||||||
|
getter: 'txpool_inspect'
|
||||||
|
}),
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'status',
|
||||||
|
getter: 'txpool_status',
|
||||||
|
outputFormatter: function(status) {
|
||||||
|
status.pending = web3._extend.utils.toDecimal(status.pending);
|
||||||
|
status.queued = web3._extend.utils.toDecimal(status.queued);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`s
|
||||||
|
|
||||||
const Admin_JS = `
|
const Admin_JS = `
|
||||||
web3._extend({
|
web3._extend({
|
||||||
property: 'admin',
|
property: 'admin',
|
||||||
|
|
@ -134,6 +326,19 @@ web3._extend({
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const Net_JS = `
|
||||||
|
web3._extend({
|
||||||
|
property: 'net',
|
||||||
|
methods: [],
|
||||||
|
properties:
|
||||||
|
[
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'version',
|
||||||
|
getter: 'net_version'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
const Debug_JS = `
|
const Debug_JS = `
|
||||||
web3._extend({
|
web3._extend({
|
||||||
property: 'debug',
|
property: 'debug',
|
||||||
|
|
|
||||||
278
rpc/api/bzz.go
278
rpc/api/bzz.go
|
|
@ -1,278 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
BzzApiVersion = "1.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
// eth api provider
|
|
||||||
// See https://github.com/ethereum/wiki/wiki/JSON-RPC
|
|
||||||
type bzzApi struct {
|
|
||||||
swarm *swarm.Swarm
|
|
||||||
methods map[string]bzzhandler
|
|
||||||
codec codec.ApiCoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// eth callback handler
|
|
||||||
type bzzhandler func(*bzzApi, *shared.Request) (interface{}, error)
|
|
||||||
|
|
||||||
var (
|
|
||||||
bzzMapping = map[string]bzzhandler{
|
|
||||||
"bzz_info": (*bzzApi).Info,
|
|
||||||
"bzz_issue": (*bzzApi).Issue,
|
|
||||||
"bzz_cash": (*bzzApi).Cash,
|
|
||||||
"bzz_deposit": (*bzzApi).Deposit,
|
|
||||||
"bzz_register": (*bzzApi).Register,
|
|
||||||
"bzz_resolve": (*bzzApi).Resolve,
|
|
||||||
"bzz_download": (*bzzApi).Download,
|
|
||||||
"bzz_upload": (*bzzApi).Upload,
|
|
||||||
"bzz_get": (*bzzApi).Get,
|
|
||||||
"bzz_put": (*bzzApi).Put,
|
|
||||||
"bzz_modify": (*bzzApi).Modify,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func newSwarmOfflineError(method string) error {
|
|
||||||
return shared.NewNotAvailableError(method, "swarm offline")
|
|
||||||
}
|
|
||||||
|
|
||||||
// create new bzzApi instance
|
|
||||||
func NewBzzApi(stack *node.Node, codec codec.Codec) *bzzApi {
|
|
||||||
var swarm *swarm.Swarm
|
|
||||||
stack.Service(&swarm)
|
|
||||||
return &bzzApi{swarm, bzzMapping, codec.New(nil)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *bzzApi) Methods() []string {
|
|
||||||
methods := make([]string, len(self.methods))
|
|
||||||
i := 0
|
|
||||||
for k := range self.methods {
|
|
||||||
methods[i] = k
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return methods
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *bzzApi) Execute(req *shared.Request) (interface{}, error) {
|
|
||||||
if callback, ok := self.methods[req.Method]; ok {
|
|
||||||
return callback(self, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, shared.NewNotImplementedError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Name() string {
|
|
||||||
return shared.BzzApiName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) ApiVersion() string {
|
|
||||||
return BzzApiVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Info(req *shared.Request) (interface{}, error) {
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
return s.Api().Info(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Issue(req *shared.Request) (interface{}, error) {
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzIssueArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
cheque, err := s.Api().Issue(common.HexToAddress(args.Beneficiary), args.Amount)
|
|
||||||
if err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := json.MarshalIndent(cheque, " ", "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(out), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Cash(req *shared.Request) (interface{}, error) {
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzCashArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.Api().Cash(args.Cheque)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Deposit(req *shared.Request) (interface{}, error) {
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzDepositArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.Api().Deposit(args.Amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Register(req *shared.Request) (interface{}, error) {
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzRegisterArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.Api().Register(common.HexToAddress(args.Address), args.Domain, common.HexToHash(args.ContentHash))
|
|
||||||
return err == nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Resolve(req *shared.Request) (interface{}, error) {
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzResolveArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := s.Api().Resolve(args.Domain)
|
|
||||||
return key.Hex(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Download(req *shared.Request) (interface{}, error) {
|
|
||||||
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzDownloadArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.Api().Download(args.BzzPath, args.LocalPath)
|
|
||||||
return err == nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Upload(req *shared.Request) (interface{}, error) {
|
|
||||||
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzUploadArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.Api().Upload(args.LocalPath, args.Index)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Get(req *shared.Request) (interface{}, error) {
|
|
||||||
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzGetArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var content []byte
|
|
||||||
var mimeType string
|
|
||||||
var status, size int
|
|
||||||
var err error
|
|
||||||
content, mimeType, status, size, err = s.Api().Get(args.Path)
|
|
||||||
|
|
||||||
obj := map[string]string{
|
|
||||||
"content": string(content),
|
|
||||||
"contentType": mimeType,
|
|
||||||
"status": fmt.Sprintf("%v", status),
|
|
||||||
"size": fmt.Sprintf("%v", size),
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Put(req *shared.Request) (interface{}, error) {
|
|
||||||
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzPutArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.Api().Put(args.Content, args.ContenType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *bzzApi) Modify(req *shared.Request) (interface{}, error) {
|
|
||||||
|
|
||||||
s := self.swarm
|
|
||||||
if s == nil {
|
|
||||||
return nil, newSwarmOfflineError(req.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := new(BzzModifyArgs)
|
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
||||||
return nil, shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.Api().Modify(args.RootHash, args.Path, args.ContentHash, args.ContentType)
|
|
||||||
}
|
|
||||||
|
|
@ -1,322 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/chequebook"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BzzDepositArgs struct {
|
|
||||||
Amount *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzDepositArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 1 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
amount, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Amount", "not a string")
|
|
||||||
}
|
|
||||||
args.Amount, ok = new(big.Int).SetString(amount, 10)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Amount", "not a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BzzCashArgs struct {
|
|
||||||
Cheque *chequebook.Cheque
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzCashArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 1 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
chequestr, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Cheque", "not a string")
|
|
||||||
}
|
|
||||||
var cheque chequebook.Cheque
|
|
||||||
err = json.Unmarshal([]byte(chequestr), &cheque)
|
|
||||||
if err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
args.Cheque = &cheque
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BzzIssueArgs struct {
|
|
||||||
Beneficiary string
|
|
||||||
Amount *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzIssueArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 2 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
beneficiary, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Amount", "not a string")
|
|
||||||
}
|
|
||||||
args.Beneficiary = beneficiary
|
|
||||||
|
|
||||||
amount, ok := obj[1].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Amount", "not a string")
|
|
||||||
}
|
|
||||||
args.Amount, ok = new(big.Int).SetString(amount, 10)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Amount", "not a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BzzRegisterArgs struct {
|
|
||||||
Address, ContentHash, Domain string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzRegisterArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 3 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
addstr, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Address", "not a string")
|
|
||||||
}
|
|
||||||
args.Address = addstr
|
|
||||||
|
|
||||||
addstr, ok = obj[1].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Domain", "not a string")
|
|
||||||
}
|
|
||||||
args.Domain = addstr
|
|
||||||
|
|
||||||
addstr, ok = obj[2].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("ContentHash", "not a string")
|
|
||||||
}
|
|
||||||
args.ContentHash = addstr
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BzzResolveArgs struct {
|
|
||||||
Domain string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzResolveArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 1 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
addstr, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Domain", "not a string")
|
|
||||||
}
|
|
||||||
args.Domain = addstr
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BzzDownloadArgs struct {
|
|
||||||
BzzPath, LocalPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzDownloadArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 2 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
addstr, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("BzzPath", "not a string")
|
|
||||||
}
|
|
||||||
args.BzzPath = addstr
|
|
||||||
|
|
||||||
addstr, ok = obj[1].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("LocalPath", "not a string")
|
|
||||||
}
|
|
||||||
args.LocalPath = addstr
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BzzUploadArgs struct {
|
|
||||||
LocalPath, Index string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzUploadArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 1 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
addstr, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("LocalPath", "not a string")
|
|
||||||
}
|
|
||||||
args.LocalPath = addstr
|
|
||||||
|
|
||||||
if len(obj) > 1 {
|
|
||||||
addstr, ok := obj[1].(string)
|
|
||||||
if ok {
|
|
||||||
args.Index = addstr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BzzGetArgs struct {
|
|
||||||
Path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzGetArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 1 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
addstr, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Path", "not a string")
|
|
||||||
}
|
|
||||||
args.Path = addstr
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BzzPutArgs struct {
|
|
||||||
Content, ContenType string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzPutArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 1 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
addstr, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Content", "not a string")
|
|
||||||
}
|
|
||||||
args.Content = addstr
|
|
||||||
|
|
||||||
addstr, ok = obj[1].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("ContenType", "not a string")
|
|
||||||
}
|
|
||||||
args.ContenType = addstr
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BzzModifyArgs struct {
|
|
||||||
RootHash, Path, ContentHash, ContentType string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args *BzzModifyArgs) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var obj []interface{}
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
|
||||||
return shared.NewDecodeParamError(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(obj) < 2 {
|
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
addstr, ok := obj[0].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("RootHash", "not a string")
|
|
||||||
}
|
|
||||||
args.RootHash = addstr
|
|
||||||
|
|
||||||
addstr, ok = obj[1].(string)
|
|
||||||
if !ok {
|
|
||||||
return shared.NewInvalidTypeError("Path", "not a string")
|
|
||||||
}
|
|
||||||
args.Path = addstr
|
|
||||||
|
|
||||||
if len(obj) >= 4 {
|
|
||||||
addstr, ok = obj[2].(string)
|
|
||||||
if ok {
|
|
||||||
args.ContentHash = addstr
|
|
||||||
}
|
|
||||||
|
|
||||||
addstr, ok = obj[3].(string)
|
|
||||||
if ok {
|
|
||||||
args.ContentType = addstr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
const Bzz_JS = `
|
|
||||||
web3._extend({
|
|
||||||
property: 'bzz',
|
|
||||||
methods:
|
|
||||||
[
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'deposit',
|
|
||||||
call: 'bzz_deposit',
|
|
||||||
params: 1,
|
|
||||||
inputFormatter: [null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'info',
|
|
||||||
call: 'bzz_info',
|
|
||||||
params: 1,
|
|
||||||
inputFormatter: [null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'cash',
|
|
||||||
call: 'bzz_cash',
|
|
||||||
params: 1,
|
|
||||||
inputFormatter: [null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'issue',
|
|
||||||
call: 'bzz_issue',
|
|
||||||
params: 2,
|
|
||||||
inputFormatter: [null, null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'register',
|
|
||||||
call: 'bzz_register',
|
|
||||||
params: 3,
|
|
||||||
inputFormatter: [null, null, null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'resolve',
|
|
||||||
call: 'bzz_resolve',
|
|
||||||
params: 1,
|
|
||||||
inputFormatter: [null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'download',
|
|
||||||
call: 'bzz_download',
|
|
||||||
params: 2,
|
|
||||||
inputFormatter: [null, null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'upload',
|
|
||||||
call: 'bzz_upload',
|
|
||||||
params: 2,
|
|
||||||
inputFormatter: [null, null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'get',
|
|
||||||
call: 'bzz_get',
|
|
||||||
params: 1,
|
|
||||||
inputFormatter: [null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'put',
|
|
||||||
call: 'bzz_put',
|
|
||||||
params: 2,
|
|
||||||
inputFormatter: [null, null]
|
|
||||||
}),
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'modify',
|
|
||||||
call: 'bzz_modify',
|
|
||||||
params: 4,
|
|
||||||
inputFormatter: [null, null, null, null]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
properties:
|
|
||||||
[
|
|
||||||
]
|
|
||||||
});
|
|
||||||
`
|
|
||||||
251
rpc/api/utils.go
251
rpc/api/utils.go
|
|
@ -1,251 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared"
|
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Mapping between the different methods each api supports
|
|
||||||
AutoCompletion = map[string][]string{
|
|
||||||
"admin": []string{
|
|
||||||
"addPeer",
|
|
||||||
"datadir",
|
|
||||||
"enableUserAgent",
|
|
||||||
"exportChain",
|
|
||||||
"getContractInfo",
|
|
||||||
"httpGet",
|
|
||||||
"importChain",
|
|
||||||
"nodeInfo",
|
|
||||||
"peers",
|
|
||||||
"register",
|
|
||||||
"registerUrl",
|
|
||||||
"saveInfo",
|
|
||||||
"setGlobalRegistrar",
|
|
||||||
"setHashReg",
|
|
||||||
"setUrlHint",
|
|
||||||
"setSolc",
|
|
||||||
"sleep",
|
|
||||||
"sleepBlocks",
|
|
||||||
"startNatSpec",
|
|
||||||
"startRPC",
|
|
||||||
"stopNatSpec",
|
|
||||||
"stopRPC",
|
|
||||||
"setGlobalRegistrar",
|
|
||||||
"setHashReg",
|
|
||||||
"setUrlHint",
|
|
||||||
"saveInfo",
|
|
||||||
"getContractInfo",
|
|
||||||
"sleep",
|
|
||||||
"httpGet",
|
|
||||||
"verbosity",
|
|
||||||
},
|
|
||||||
"bzz": []string{
|
|
||||||
"info",
|
|
||||||
"issue",
|
|
||||||
"cash",
|
|
||||||
"deposit",
|
|
||||||
"register",
|
|
||||||
"resolve",
|
|
||||||
"download",
|
|
||||||
"upload",
|
|
||||||
"get",
|
|
||||||
"put",
|
|
||||||
"modify",
|
|
||||||
},
|
|
||||||
"db": []string{
|
|
||||||
"getString",
|
|
||||||
"putString",
|
|
||||||
"getHex",
|
|
||||||
"putHex",
|
|
||||||
},
|
|
||||||
"debug": []string{
|
|
||||||
"dumpBlock",
|
|
||||||
"getBlockRlp",
|
|
||||||
"metrics",
|
|
||||||
"printBlock",
|
|
||||||
"processBlock",
|
|
||||||
"seedHash",
|
|
||||||
"setHead",
|
|
||||||
},
|
|
||||||
"eth": []string{
|
|
||||||
"accounts",
|
|
||||||
"blockNumber",
|
|
||||||
"call",
|
|
||||||
"contract",
|
|
||||||
"coinbase",
|
|
||||||
"compile.lll",
|
|
||||||
"compile.serpent",
|
|
||||||
"compile.solidity",
|
|
||||||
"contract",
|
|
||||||
"defaultAccount",
|
|
||||||
"defaultBlock",
|
|
||||||
"estimateGas",
|
|
||||||
"filter",
|
|
||||||
"getBalance",
|
|
||||||
"getBlock",
|
|
||||||
"getBlockTransactionCount",
|
|
||||||
"getBlockUncleCount",
|
|
||||||
"getCode",
|
|
||||||
"getNatSpec",
|
|
||||||
"getCompilers",
|
|
||||||
"gasPrice",
|
|
||||||
"getStorageAt",
|
|
||||||
"getTransaction",
|
|
||||||
"getTransactionCount",
|
|
||||||
"getTransactionFromBlock",
|
|
||||||
"getTransactionReceipt",
|
|
||||||
"getUncle",
|
|
||||||
"hashrate",
|
|
||||||
"mining",
|
|
||||||
"namereg",
|
|
||||||
"getPendingTransactions",
|
|
||||||
"resend",
|
|
||||||
"sendRawTransaction",
|
|
||||||
"sendTransaction",
|
|
||||||
"sign",
|
|
||||||
"syncing",
|
|
||||||
},
|
|
||||||
"miner": []string{
|
|
||||||
"hashrate",
|
|
||||||
"makeDAG",
|
|
||||||
"setEtherbase",
|
|
||||||
"setExtra",
|
|
||||||
"setGasPrice",
|
|
||||||
"startAutoDAG",
|
|
||||||
"setEtherbase",
|
|
||||||
"start",
|
|
||||||
"stopAutoDAG",
|
|
||||||
"stop",
|
|
||||||
},
|
|
||||||
"net": []string{
|
|
||||||
"peerCount",
|
|
||||||
"listening",
|
|
||||||
},
|
|
||||||
"personal": []string{
|
|
||||||
"listAccounts",
|
|
||||||
"newAccount",
|
|
||||||
"unlockAccount",
|
|
||||||
},
|
|
||||||
"shh": []string{
|
|
||||||
"post",
|
|
||||||
"newIdentity",
|
|
||||||
"hasIdentity",
|
|
||||||
"newGroup",
|
|
||||||
"addToGroup",
|
|
||||||
"filter",
|
|
||||||
},
|
|
||||||
"txpool": []string{
|
|
||||||
"status",
|
|
||||||
},
|
|
||||||
"web3": []string{
|
|
||||||
"sha3",
|
|
||||||
"version",
|
|
||||||
"fromWei",
|
|
||||||
"toWei",
|
|
||||||
"toHex",
|
|
||||||
"toAscii",
|
|
||||||
"fromAscii",
|
|
||||||
"toBigNumber",
|
|
||||||
"isAddress",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Parse a comma separated API string to individual api's
|
|
||||||
func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, stack *node.Node) ([]shared.EthereumApi, error) {
|
|
||||||
if len(strings.TrimSpace(apistr)) == 0 {
|
|
||||||
return nil, fmt.Errorf("Empty apistr provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
names := strings.Split(apistr, ",")
|
|
||||||
apis := make([]shared.EthereumApi, len(names))
|
|
||||||
|
|
||||||
var eth *eth.Ethereum
|
|
||||||
if stack != nil {
|
|
||||||
if err := stack.Service(ð); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i, name := range names {
|
|
||||||
switch strings.ToLower(strings.TrimSpace(name)) {
|
|
||||||
case shared.AdminApiName:
|
|
||||||
apis[i] = NewAdminApi(xeth, stack, codec)
|
|
||||||
case shared.BzzApiName:
|
|
||||||
apis[i] = NewBzzApi(stack, codec)
|
|
||||||
case shared.DebugApiName:
|
|
||||||
apis[i] = NewDebugApi(xeth, eth, codec)
|
|
||||||
case shared.DbApiName:
|
|
||||||
apis[i] = NewDbApi(xeth, eth, codec)
|
|
||||||
case shared.EthApiName:
|
|
||||||
apis[i] = NewEthApi(xeth, eth, codec)
|
|
||||||
case shared.MinerApiName:
|
|
||||||
apis[i] = NewMinerApi(eth, codec)
|
|
||||||
case shared.NetApiName:
|
|
||||||
apis[i] = NewNetApi(xeth, eth, codec)
|
|
||||||
case shared.ShhApiName:
|
|
||||||
apis[i] = NewShhApi(xeth, eth, codec)
|
|
||||||
case shared.TxPoolApiName:
|
|
||||||
apis[i] = NewTxPoolApi(xeth, eth, codec)
|
|
||||||
case shared.PersonalApiName:
|
|
||||||
apis[i] = NewPersonalApi(xeth, eth, codec)
|
|
||||||
case shared.Web3ApiName:
|
|
||||||
apis[i] = NewWeb3Api(xeth, codec)
|
|
||||||
case "rpc": // gives information about the RPC interface
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("Unknown API '%s'", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return apis, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Javascript(name string) string {
|
|
||||||
switch strings.ToLower(strings.TrimSpace(name)) {
|
|
||||||
case shared.AdminApiName:
|
|
||||||
return Admin_JS
|
|
||||||
case shared.BzzApiName:
|
|
||||||
return Bzz_JS
|
|
||||||
case shared.DebugApiName:
|
|
||||||
return Debug_JS
|
|
||||||
case shared.DbApiName:
|
|
||||||
return Db_JS
|
|
||||||
case shared.EthApiName:
|
|
||||||
return Eth_JS
|
|
||||||
case shared.MinerApiName:
|
|
||||||
return Miner_JS
|
|
||||||
case shared.NetApiName:
|
|
||||||
return Net_JS
|
|
||||||
case shared.ShhApiName:
|
|
||||||
return Shh_JS
|
|
||||||
case shared.TxPoolApiName:
|
|
||||||
return TxPool_JS
|
|
||||||
case shared.PersonalApiName:
|
|
||||||
return Personal_JS
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
487
swarm/api/api.go
487
swarm/api/api.go
|
|
@ -1,24 +1,16 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/chequebook"
|
|
||||||
"github.com/ethereum/go-ethereum/common/registrar"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -27,68 +19,91 @@ var (
|
||||||
domainAndVersion = regexp.MustCompile("[@:;,]+")
|
domainAndVersion = regexp.MustCompile("[@:;,]+")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Resolver interface {
|
||||||
|
Resolve(string) (storage.Key, error)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Api implements webserver/file system related content storage and retrieval
|
Api implements webserver/file system related content storage and retrieval
|
||||||
on top of the dpa
|
on top of the dpa
|
||||||
it is the public interface of the dpa which is included in the ethereum stack
|
it is the public interface of the dpa which is included in the ethereum stack
|
||||||
*/
|
*/
|
||||||
type Api struct {
|
type Api struct {
|
||||||
dpa *storage.DPA
|
dpa *storage.DPA
|
||||||
registrar registrar.VersionedRegistrar
|
dns Resolver
|
||||||
conf *Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//the api constructor initialises
|
//the api constructor initialises
|
||||||
func NewApi(dpa *storage.DPA, registrar registrar.VersionedRegistrar, conf *Config) (self *Api) {
|
func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) {
|
||||||
return &Api{dpa, registrar, conf}
|
self = &Api{
|
||||||
}
|
dpa: dpa,
|
||||||
|
dns: dns,
|
||||||
// this should move over to chequebook ipc api
|
|
||||||
func (self *Api) Issue(beneficiary common.Address, amount *big.Int) (cheque *chequebook.Cheque, err error) {
|
|
||||||
return self.conf.Swap.Chequebook().Issue(beneficiary, amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Api) Cash(cheque *chequebook.Cheque) (txhash string, err error) {
|
|
||||||
return self.conf.Swap.Chequebook().Cash(cheque)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Api) Deposit(amount *big.Int) (txhash string, err error) {
|
|
||||||
return self.conf.Swap.Chequebook().Deposit(amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// serialisable info about swarm
|
|
||||||
type Info struct {
|
|
||||||
*Config
|
|
||||||
*chequebook.Params
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Api) Info() *Info {
|
|
||||||
|
|
||||||
return &Info{
|
|
||||||
Config: self.conf,
|
|
||||||
Params: chequebook.ContractParams,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get uses iterative manifest retrieval and prefix matching
|
|
||||||
// to resolve path to content using dpa retrieve
|
|
||||||
func (self *Api) Get(bzzpath string) (content []byte, mimeType string, status int, size int, err error) {
|
|
||||||
var reader storage.SectionReader
|
|
||||||
reader, mimeType, status, err = self.getPath("/" + bzzpath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
content = make([]byte, reader.Size())
|
|
||||||
size, err = reader.Read(content)
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put provides singleton manifest creation and optional name registration
|
// DPA reader API
|
||||||
// on top of dpa store
|
func (self *Api) Retrieve(key storage.Key) storage.SectionReader {
|
||||||
|
return self.dpa.Retrieve(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Api) Store(data storage.SectionReader, wg *sync.WaitGroup) (key storage.Key, err error) {
|
||||||
|
return self.dpa.Store(data, wg)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrResolve error
|
||||||
|
|
||||||
|
// DNS Resolver
|
||||||
|
func (self *Api) Resolve(hostPort string, nameresolver bool) (contentHash storage.Key, err error) {
|
||||||
|
if hashMatcher.MatchString(hostPort) || self.dns == nil {
|
||||||
|
glog.V(logger.Detail).Infof("[BZZ] host is a contentHash: '%v'", hostPort)
|
||||||
|
return storage.Key(common.Hex2Bytes(hostPort)), nil
|
||||||
|
}
|
||||||
|
if !nameresolver {
|
||||||
|
err = fmt.Errorf("'%s' is not a content hash value.", hostPort)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
contentHash, err = self.dns.Resolve(hostPort)
|
||||||
|
if err != nil {
|
||||||
|
err = ErrResolve(err)
|
||||||
|
glog.V(logger.Warn).Infof("[BZZ] DNS error : %v", err)
|
||||||
|
}
|
||||||
|
glog.V(logger.Detail).Infof("[BZZ] host lookup: %v -> %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(uri string) (hostPort, path string) {
|
||||||
|
parts := slashes.Split(uri, 3)
|
||||||
|
var i int
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// beginning with slash is now optional
|
||||||
|
for len(parts[i]) == 0 {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
hostPort = parts[i]
|
||||||
|
for i < len(parts)-1 {
|
||||||
|
i++
|
||||||
|
if len(path) > 0 {
|
||||||
|
path = path + "/" + parts[i]
|
||||||
|
} else {
|
||||||
|
path = parts[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] Swarm: host: '%s', path '%s' requested.", hostPort, path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Api) parseAndResolve(uri string, nameresolver bool) (contentHash storage.Key, hostPort, path string, err error) {
|
||||||
|
hostPort, path = parse(uri)
|
||||||
|
//resolving host and port
|
||||||
|
contentHash, err = self.Resolve(hostPort, nameresolver)
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] Resolved '%s' to contentHash: '%s', path: '%s'", uri, contentHash, path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put provides singleton manifest creation on top of dpa store
|
||||||
func (self *Api) Put(content, contentType string) (string, error) {
|
func (self *Api) Put(content, contentType string) (string, error) {
|
||||||
sr := io.NewSectionReader(strings.NewReader(content), 0, int64(len(content)))
|
sr := io.NewSectionReader(strings.NewReader(content), 0, int64(len(content)))
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
|
|
@ -106,8 +121,36 @@ func (self *Api) Put(content, contentType string) (string, error) {
|
||||||
return key.String(), nil
|
return key.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Api) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) {
|
// Get uses iterative manifest retrieval and prefix matching
|
||||||
root := common.Hex2Bytes(rootHash)
|
// to resolve path to content using dpa retrieve
|
||||||
|
// it returns a section reader, mimeType, status and an error
|
||||||
|
func (self *Api) Get(uri string, nameresolver bool) (reader storage.SectionReader, mimeType string, status int, err error) {
|
||||||
|
|
||||||
|
key, _, path, err := self.parseAndResolve(uri, nameresolver)
|
||||||
|
|
||||||
|
trie, err := loadManifest(self.dpa, key)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(logger.Warn).Infof("[BZZ] Swarm: loadManifestTrie error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(logger.Detail).Infof("[BZZ] Swarm: getEntry(%s)", path)
|
||||||
|
entry, _ := trie.getEntry(path)
|
||||||
|
if entry != nil {
|
||||||
|
key = common.Hex2Bytes(entry.Hash)
|
||||||
|
status = entry.Status
|
||||||
|
mimeType = entry.ContentType
|
||||||
|
glog.V(logger.Detail).Infof("[BZZ] Swarm: content lookup key: '%v' (%v)", key, mimeType)
|
||||||
|
reader = self.dpa.Retrieve(key)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("manifest entry for '%s' not found", path)
|
||||||
|
glog.V(logger.Warn).Infof("[BZZ] Swarm: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Api) Modify(uri, contentHash, contentType string, nameresolver bool) (newRootHash string, err error) {
|
||||||
|
root, _, path, err := self.parseAndResolve(uri, nameresolver)
|
||||||
trie, err := loadManifest(self.dpa, root)
|
trie, err := loadManifest(self.dpa, root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -130,327 +173,3 @@ func (self *Api) Modify(rootHash, path, contentHash, contentType string) (newRoo
|
||||||
}
|
}
|
||||||
return trie.hash.String(), nil
|
return trie.hash.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxParallelFiles = 5
|
|
||||||
|
|
||||||
// Download replicates the manifest path structure on the local filesystem
|
|
||||||
// under localpath
|
|
||||||
func (self *Api) Download(bzzpath, localpath string) (err error) {
|
|
||||||
lpath, err := filepath.Abs(filepath.Clean(localpath))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = os.MkdirAll(lpath, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := slashes.Split(bzzpath, 3)
|
|
||||||
if len(parts) < 2 {
|
|
||||||
return fmt.Errorf("Invalid bzz path")
|
|
||||||
}
|
|
||||||
hostPort := parts[1]
|
|
||||||
var path string
|
|
||||||
if len(parts) > 2 {
|
|
||||||
path = regularSlashes(parts[2]) + "/"
|
|
||||||
}
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: host: '%s', path '%s' requested.", hostPort, path)
|
|
||||||
|
|
||||||
//resolving host and port
|
|
||||||
var key storage.Key
|
|
||||||
key, err = self.Resolve(hostPort)
|
|
||||||
if err != nil {
|
|
||||||
err = errResolve(err)
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: error : %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
trie, err := loadManifest(self.dpa, key)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: loadManifestTrie error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type downloadListEntry struct {
|
|
||||||
key storage.Key
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
var list []*downloadListEntry
|
|
||||||
var mde, mderr error
|
|
||||||
|
|
||||||
prevPath := lpath
|
|
||||||
err = trie.listWithPrefix(path, func(entry *manifestTrieEntry, suffix string) { // TODO: paralellize
|
|
||||||
key := common.Hex2Bytes(entry.Hash)
|
|
||||||
path := lpath + "/" + suffix
|
|
||||||
dir := filepath.Dir(path)
|
|
||||||
if dir != prevPath {
|
|
||||||
mde = os.MkdirAll(dir, os.ModePerm)
|
|
||||||
if mde != nil {
|
|
||||||
mderr = mde
|
|
||||||
}
|
|
||||||
prevPath = dir
|
|
||||||
}
|
|
||||||
if (mde == nil) && (path != dir+"/") {
|
|
||||||
list = append(list, &downloadListEntry{key: key, path: path})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
err = mderr
|
|
||||||
}
|
|
||||||
|
|
||||||
cnt := len(list)
|
|
||||||
errors := make([]error, cnt)
|
|
||||||
done := make(chan bool, maxParallelFiles)
|
|
||||||
dcnt := 0
|
|
||||||
|
|
||||||
for i, entry := range list {
|
|
||||||
if i >= dcnt+maxParallelFiles {
|
|
||||||
<-done
|
|
||||||
dcnt++
|
|
||||||
}
|
|
||||||
go func(i int, entry *downloadListEntry, done chan bool) {
|
|
||||||
f, err := os.Create(entry.path) // TODO: path separators
|
|
||||||
if err == nil {
|
|
||||||
reader := self.dpa.Retrieve(entry.key)
|
|
||||||
writer := bufio.NewWriter(f)
|
|
||||||
_, err = io.CopyN(writer, reader, reader.Size()) // TODO: handle errors
|
|
||||||
err2 := writer.Flush()
|
|
||||||
if err == nil {
|
|
||||||
err = err2
|
|
||||||
}
|
|
||||||
err2 = f.Close()
|
|
||||||
if err == nil {
|
|
||||||
err = err2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errors[i] = err
|
|
||||||
done <- true
|
|
||||||
}(i, entry, done)
|
|
||||||
}
|
|
||||||
for dcnt < cnt {
|
|
||||||
<-done
|
|
||||||
dcnt++
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i, _ := range list {
|
|
||||||
if errors[i] != nil {
|
|
||||||
return errors[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload replicates a local directory as a manifest file and uploads it
|
|
||||||
// using dpa store
|
|
||||||
// TODO: localpath should point to a manifest
|
|
||||||
func (self *Api) Upload(lpath, index string) (string, error) {
|
|
||||||
var list []*manifestTrieEntry
|
|
||||||
localpath, err := filepath.Abs(filepath.Clean(lpath))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Open(localpath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
stat, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var start int
|
|
||||||
if stat.IsDir() {
|
|
||||||
start = len(localpath)
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] uploading '%s'", localpath)
|
|
||||||
err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if (err == nil) && !info.IsDir() {
|
|
||||||
//fmt.Printf("lp %s path %s\n", localpath, path)
|
|
||||||
if len(path) <= start {
|
|
||||||
return fmt.Errorf("Path is too short")
|
|
||||||
}
|
|
||||||
if path[:start] != localpath {
|
|
||||||
return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath)
|
|
||||||
}
|
|
||||||
entry := &manifestTrieEntry{
|
|
||||||
Path: path,
|
|
||||||
}
|
|
||||||
list = append(list, entry)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dir := filepath.Dir(localpath)
|
|
||||||
start = len(dir)
|
|
||||||
if len(localpath) <= start {
|
|
||||||
return "", fmt.Errorf("Path is too short")
|
|
||||||
}
|
|
||||||
if localpath[:start] != dir {
|
|
||||||
return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir)
|
|
||||||
}
|
|
||||||
entry := &manifestTrieEntry{
|
|
||||||
Path: localpath,
|
|
||||||
}
|
|
||||||
list = append(list, entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
cnt := len(list)
|
|
||||||
errors := make([]error, cnt)
|
|
||||||
done := make(chan bool, maxParallelFiles)
|
|
||||||
dcnt := 0
|
|
||||||
|
|
||||||
for i, entry := range list {
|
|
||||||
if i >= dcnt+maxParallelFiles {
|
|
||||||
<-done
|
|
||||||
dcnt++
|
|
||||||
}
|
|
||||||
go func(i int, entry *manifestTrieEntry, done chan bool) {
|
|
||||||
f, err := os.Open(entry.Path)
|
|
||||||
if err == nil {
|
|
||||||
stat, _ := f.Stat()
|
|
||||||
sr := io.NewSectionReader(f, 0, stat.Size())
|
|
||||||
wg := &sync.WaitGroup{}
|
|
||||||
var hash storage.Key
|
|
||||||
hash, err = self.dpa.Store(sr, wg)
|
|
||||||
if hash != nil {
|
|
||||||
list[i].Hash = hash.String()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
if err == nil {
|
|
||||||
first512 := make([]byte, 512)
|
|
||||||
fread, _ := sr.ReadAt(first512, 0)
|
|
||||||
if fread > 0 {
|
|
||||||
mimeType := http.DetectContentType(first512[:fread])
|
|
||||||
if filepath.Ext(entry.Path) == ".css" {
|
|
||||||
mimeType = "text/css"
|
|
||||||
}
|
|
||||||
list[i].ContentType = mimeType
|
|
||||||
//fmt.Printf("%v %v %v\n", entry.Path, mimeType, filepath.Ext(entry.Path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
errors[i] = err
|
|
||||||
done <- true
|
|
||||||
}(i, entry, done)
|
|
||||||
}
|
|
||||||
for dcnt < cnt {
|
|
||||||
<-done
|
|
||||||
dcnt++
|
|
||||||
}
|
|
||||||
|
|
||||||
trie := &manifestTrie{
|
|
||||||
dpa: self.dpa,
|
|
||||||
}
|
|
||||||
for i, entry := range list {
|
|
||||||
if errors[i] != nil {
|
|
||||||
return "", errors[i]
|
|
||||||
}
|
|
||||||
entry.Path = regularSlashes(entry.Path[start:])
|
|
||||||
if entry.Path == index {
|
|
||||||
ientry := &manifestTrieEntry{
|
|
||||||
Path: "",
|
|
||||||
Hash: entry.Hash,
|
|
||||||
ContentType: entry.ContentType,
|
|
||||||
}
|
|
||||||
trie.addEntry(ientry)
|
|
||||||
}
|
|
||||||
trie.addEntry(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
err2 := trie.recalcAndStore()
|
|
||||||
var hs string
|
|
||||||
if err2 == nil {
|
|
||||||
hs = trie.hash.String()
|
|
||||||
}
|
|
||||||
return hs, err2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Api) Register(sender common.Address, domain string, hash common.Hash) (err error) {
|
|
||||||
domainhash := common.BytesToHash(crypto.Sha3([]byte(domain)))
|
|
||||||
|
|
||||||
if self.registrar != nil {
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: host '%s' (hash: '%v') to be registered as '%v'", domain, domainhash.Hex(), hash.Hex())
|
|
||||||
_, err = self.registrar.Registry().SetHashToHash(sender, domainhash, hash)
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("no registry: %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type errResolve error
|
|
||||||
|
|
||||||
func (self *Api) Resolve(hostPort string) (contentHash storage.Key, err error) {
|
|
||||||
host := hostPort
|
|
||||||
if hashMatcher.MatchString(host) {
|
|
||||||
contentHash = storage.Key(common.Hex2Bytes(host))
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: host is a contentHash: '%v'", contentHash)
|
|
||||||
} else {
|
|
||||||
if self.registrar != nil {
|
|
||||||
var hash common.Hash
|
|
||||||
var version *big.Int
|
|
||||||
parts := domainAndVersion.Split(host, 3)
|
|
||||||
if len(parts) > 1 && parts[1] != "" {
|
|
||||||
host = parts[0]
|
|
||||||
version = common.Big(parts[1])
|
|
||||||
}
|
|
||||||
hostHash := crypto.Sha3Hash([]byte(host))
|
|
||||||
hash, err = self.registrar.Resolver(version).HashToHash(hostHash)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("unable to resolve '%s': %v", hostPort, err)
|
|
||||||
}
|
|
||||||
contentHash = storage.Key(hash.Bytes())
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: resolve host '%s' to contentHash: '%v'", hostPort, contentHash)
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("no resolver '%s': %v", hostPort, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Api) getPath(uri string) (reader storage.SectionReader, mimeType string, status int, err error) {
|
|
||||||
parts := slashes.Split(uri, 3)
|
|
||||||
hostPort := parts[1]
|
|
||||||
var path string
|
|
||||||
if len(parts) > 2 {
|
|
||||||
path = parts[2]
|
|
||||||
}
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: host: '%s', path '%s' requested.", hostPort, path)
|
|
||||||
|
|
||||||
//resolving host and port
|
|
||||||
var key storage.Key
|
|
||||||
key, err = self.Resolve(hostPort)
|
|
||||||
if err != nil {
|
|
||||||
err = errResolve(err)
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: error : %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
trie, err := loadManifest(self.dpa, key)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: loadManifestTrie error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: getEntry(%s)", path)
|
|
||||||
entry, _ := trie.getEntry(path)
|
|
||||||
if entry != nil {
|
|
||||||
key = common.Hex2Bytes(entry.Hash)
|
|
||||||
status = entry.Status
|
|
||||||
mimeType = entry.ContentType
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: content lookup key: '%v' (%v)", key, mimeType)
|
|
||||||
reader = self.dpa.Retrieve(key)
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("manifest entry for '%s' not found", path)
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,205 +1,86 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
// "bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
//TODO: add tests for resolver/registrar
|
func testApi(t *testing.T, f func(*Api)) {
|
||||||
// will most likely be its own package under service?
|
|
||||||
|
|
||||||
var (
|
|
||||||
testDir string
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
_, filename, _, _ := runtime.Caller(1)
|
|
||||||
testDir = path.Join(path.Dir(filename), "../test")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testApi() (api *Api, err error) {
|
|
||||||
datadir, err := ioutil.TempDir("", "bzz-test")
|
datadir, err := ioutil.TempDir("", "bzz-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
t.Fatalf("unable to create temp dir: %v", err)
|
||||||
}
|
}
|
||||||
os.RemoveAll(datadir)
|
os.RemoveAll(datadir)
|
||||||
|
defer os.RemoveAll(datadir)
|
||||||
dpa, err := storage.NewLocalDPA(datadir)
|
dpa, err := storage.NewLocalDPA(datadir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
prvkey, _ := crypto.GenerateKey()
|
api := NewApi(dpa, nil)
|
||||||
|
dpa.Start()
|
||||||
|
f(api)
|
||||||
|
dpa.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
config, err := NewConfig(datadir, common.Address{}, prvkey)
|
type testResponse struct {
|
||||||
if err != nil {
|
reader storage.SectionReader
|
||||||
return
|
*Response
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResponse(t *testing.T, resp *testResponse, exp *Response) {
|
||||||
|
|
||||||
|
if resp.MimeType != exp.MimeType {
|
||||||
|
t.Errorf("incorrect mimeType. expected '%s', got '%s'", exp.MimeType, resp.MimeType)
|
||||||
}
|
}
|
||||||
api = NewApi(dpa, nil, config)
|
if resp.Status != exp.Status {
|
||||||
api.dpa.Start()
|
t.Errorf("incorrect status. expected '%d', got '%d'", exp.Status, resp.Status)
|
||||||
|
}
|
||||||
|
if resp.Size != exp.Size {
|
||||||
|
t.Errorf("incorrect size. expected '%d', got '%d'", exp.Size, resp.Size)
|
||||||
|
}
|
||||||
|
if resp.reader != nil {
|
||||||
|
content := make([]byte, resp.Size)
|
||||||
|
read, _ := resp.reader.Read(content)
|
||||||
|
if int64(read) != exp.Size {
|
||||||
|
t.Errorf("incorrect content length. expected '%s...', got '%s...'", read, exp.Size)
|
||||||
|
}
|
||||||
|
resp.Content = string(content)
|
||||||
|
}
|
||||||
|
if resp.Content != exp.Content {
|
||||||
|
// if !bytes.Equal(resp.Content, exp.Content) {
|
||||||
|
t.Errorf("incorrect content. expected '%s...', got '%s...'", string(exp.Content), string(resp.Content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
// func expResponse(content []byte, mimeType string, status int) *Response {
|
||||||
|
func expResponse(content string, mimeType string, status int) *Response {
|
||||||
|
return &Response{mimeType, status, int64(len(content)), content}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func testGet(t *testing.T, api *Api, bzzhash string) *testResponse {
|
||||||
|
func testGet(t *testing.T, api *Api, bzzhash string) *testResponse {
|
||||||
|
reader, mimeType, status, err := api.Get(bzzhash, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
return &testResponse{reader, &Response{mimeType, status, reader.Size(), ""}}
|
||||||
|
// return &testResponse{reader, &Response{mimeType, status, reader.Size(), nil}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApiPut(t *testing.T) {
|
func TestApiPut(t *testing.T) {
|
||||||
api, err := testApi()
|
testApi(t, func(api *Api) {
|
||||||
if err != nil {
|
content := "hello"
|
||||||
t.Errorf("unexpected error: %v", err)
|
exp := expResponse(content, "text/plain", 0)
|
||||||
return
|
// exp := expResponse([]byte(content), "text/plain", 0)
|
||||||
}
|
bzzhash, err := api.Put(content, exp.MimeType)
|
||||||
defer api.dpa.Stop()
|
if err != nil {
|
||||||
expContent := "hello"
|
t.Fatalf("unexpected error: %v", err)
|
||||||
expMimeType := "text/plain"
|
}
|
||||||
expStatus := 0
|
resp := testGet(t, api, bzzhash)
|
||||||
expSize := len(expContent)
|
checkResponse(t, resp, exp)
|
||||||
bzzhash, err := api.Put(expContent, expMimeType)
|
})
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
testGet(t, api, bzzhash, []byte(expContent), expMimeType, expStatus, expSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGet(t *testing.T, api *Api, bzzhash string, expContent []byte, expMimeType string, expStatus int, expSize int) {
|
|
||||||
content, mimeType, status, size, err := api.Get(bzzhash)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !bytes.Equal(content, expContent) {
|
|
||||||
t.Errorf("incorrect content. expected '%s...', got '%s...'", string(expContent), string(content))
|
|
||||||
}
|
|
||||||
if mimeType != expMimeType {
|
|
||||||
t.Errorf("incorrect mimeType. expected '%s', got '%s'", expMimeType, mimeType)
|
|
||||||
}
|
|
||||||
if status != expStatus {
|
|
||||||
t.Errorf("incorrect status. expected '%d', got '%d'", expStatus, status)
|
|
||||||
}
|
|
||||||
if size != expSize {
|
|
||||||
t.Errorf("incorrect size. expected '%d', got '%d'", expSize, size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiDirUpload(t *testing.T) {
|
|
||||||
t.Skip("FIXME")
|
|
||||||
api, err := testApi()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bzzhash, err := api.Upload(path.Join(testDir, "test0"), "")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
|
|
||||||
testGet(t, api, path.Join(bzzhash, "index.html"), content, "text/html; charset=utf-8", 0, 202)
|
|
||||||
|
|
||||||
content, err = ioutil.ReadFile(path.Join(testDir, "test0", "index.css"))
|
|
||||||
testGet(t, api, path.Join(bzzhash, "index.css"), content, "text/css", 0, 132)
|
|
||||||
|
|
||||||
content, err = ioutil.ReadFile(path.Join(testDir, "test0", "img", "logo.png"))
|
|
||||||
testGet(t, api, path.Join(bzzhash, "img", "logo.png"), content, "image/png", 0, 18136)
|
|
||||||
|
|
||||||
_, _, _, _, err = api.Get(bzzhash)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiDirUploadModify(t *testing.T) {
|
|
||||||
t.Skip("FIXME")
|
|
||||||
api, err := testApi()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bzzhash, err := api.Upload(path.Join(testDir, "test0"), "")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bzzhash, err = api.Modify(bzzhash, "index.html", "", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bzzhash, err = api.Modify(bzzhash, "index2.html", "9ea1f60ebd80786d6005f6b256376bdb494a82496cd86fe8c307cdfb23c99e71", "text/html; charset=utf-8")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bzzhash, err = api.Modify(bzzhash, "img/logo.png", "9ea1f60ebd80786d6005f6b256376bdb494a82496cd86fe8c307cdfb23c99e71", "text/html; charset=utf-8")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
|
|
||||||
testGet(t, api, path.Join(bzzhash, "index2.html"), content, "text/html; charset=utf-8", 0, 202)
|
|
||||||
testGet(t, api, path.Join(bzzhash, "img", "logo.png"), content, "text/html; charset=utf-8", 0, 202)
|
|
||||||
|
|
||||||
content, err = ioutil.ReadFile(path.Join(testDir, "test0", "index.css"))
|
|
||||||
testGet(t, api, path.Join(bzzhash, "index.css"), content, "text/css", 0, 132)
|
|
||||||
|
|
||||||
_, _, _, _, err = api.Get(bzzhash)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiDirUploadWithRootFile(t *testing.T) {
|
|
||||||
api, err := testApi()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bzzhash, err := api.Upload(path.Join(testDir, "test0"), "index.html")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
|
|
||||||
testGet(t, api, bzzhash, content, "text/html; charset=utf-8", 0, 202)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiFileUpload(t *testing.T) {
|
|
||||||
api, err := testApi()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bzzhash, err := api.Upload(path.Join(testDir, "test0", "index.html"), "")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
|
|
||||||
testGet(t, api, path.Join(bzzhash, "index.html"), content, "text/html; charset=utf-8", 0, 202)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApiFileUploadWithRootFile(t *testing.T) {
|
|
||||||
api, err := testApi()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bzzhash, err := api.Upload(path.Join(testDir, "test0", "index.html"), "index.html")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(path.Join(testDir, "test0", "index.html"))
|
|
||||||
testGet(t, api, bzzhash, content, "text/html; charset=utf-8", 0, 202)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ func TestConfigWriteRead(t *testing.T) {
|
||||||
t.Fatalf("default config file cannot be read: %v", err)
|
t.Fatalf("default config file cannot be read: %v", err)
|
||||||
}
|
}
|
||||||
exp := strings.Replace(defaultConfig, "TMPDIR", tmp, -1)
|
exp := strings.Replace(defaultConfig, "TMPDIR", tmp, -1)
|
||||||
|
exp = strings.Replace(exp, "\\", "\\\\", -1)
|
||||||
|
|
||||||
if string(data) != exp {
|
if string(data) != exp {
|
||||||
t.Fatalf("default config mismatch:\nexpected:\n'%v'\ngot:\n'%v'", exp, string(data))
|
t.Fatalf("default config mismatch:\nexpected:\n'%v'\ngot:\n'%v'", exp, string(data))
|
||||||
|
|
|
||||||
|
|
@ -1,320 +0,0 @@
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"regexp"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/registrar"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
!! THIS IS CURRENTLY A PLACEHOLEDER (HACK) UNTIL PRC v2
|
|
||||||
!! https://github.com/ethereum/go-ethereum/pull/1912 is merged
|
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
*/
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultGasPrice = big.NewInt(10000000000000) //150000000000
|
|
||||||
defaultGas = big.NewInt(90000) //500000
|
|
||||||
addrReg = regexp.MustCompile(`^(0x)?[a-fA-F0-9]{40}$`)
|
|
||||||
)
|
|
||||||
|
|
||||||
type ethApi struct {
|
|
||||||
eth *eth.Ethereum
|
|
||||||
gpo *eth.GasPriceOracle
|
|
||||||
transactionMu sync.RWMutex
|
|
||||||
transactMu sync.RWMutex
|
|
||||||
state *state.StateDB
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEthApi(ethereum *eth.Ethereum) *ethApi {
|
|
||||||
return ðApi{
|
|
||||||
eth: ethereum,
|
|
||||||
gpo: eth.NewGasPriceOracle(ethereum),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// subscribes to new head block events and
|
|
||||||
// waits until blockchain height is greater n at any time
|
|
||||||
// given the current head, waits for the next chain event
|
|
||||||
// sets the state to the current head
|
|
||||||
// loop is async and quit by closing the channel
|
|
||||||
// used in tests and JS console debug module to control advancing private chain manually
|
|
||||||
// Note: this is not threadsafe, only called in JS single process and tests
|
|
||||||
func (self *ethApi) UpdateState() (wait chan *big.Int) {
|
|
||||||
wait = make(chan *big.Int)
|
|
||||||
self.state, _ = state.New(self.eth.BlockChain().GetBlockByNumber(0).Root(), self.eth.ChainDb())
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
eventSub := self.eth.EventMux().Subscribe(core.ChainHeadEvent{})
|
|
||||||
defer eventSub.Unsubscribe()
|
|
||||||
|
|
||||||
var m, n *big.Int
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
eventCh := eventSub.Chan()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event, ok := <-eventCh:
|
|
||||||
if !ok {
|
|
||||||
// Event subscription closed, set the channel to nil to stop spinning
|
|
||||||
eventCh = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// A real event arrived, process if new head block assignment
|
|
||||||
if event, ok := event.Data.(core.ChainHeadEvent); ok {
|
|
||||||
m = event.Block.Number()
|
|
||||||
if n != nil && n.Cmp(m) < 0 {
|
|
||||||
wait <- n
|
|
||||||
n = nil
|
|
||||||
}
|
|
||||||
statedb, err := state.New(event.Block.Root(), self.eth.ChainDb())
|
|
||||||
if err != nil {
|
|
||||||
glog.V(logger.Error).Infoln("Could not create new state: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.state = statedb
|
|
||||||
}
|
|
||||||
case n, ok = <-wait:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ethApi) AtStateNum(num int64) registrar.Backend {
|
|
||||||
var st *state.StateDB
|
|
||||||
var err error
|
|
||||||
switch num {
|
|
||||||
case -2:
|
|
||||||
st = self.eth.Miner().PendingState().Copy()
|
|
||||||
default:
|
|
||||||
if block := self.getBlockByHeight(num); block != nil {
|
|
||||||
st, err = state.New(block.Root(), self.eth.ChainDb())
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
st, err = state.New(self.eth.BlockChain().GetBlockByNumber(0).Root(), self.eth.ChainDb())
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return registrar.Backend(ðApi{
|
|
||||||
eth: self.eth,
|
|
||||||
state: st,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
func (self *ethApi) GetTxReceipt(txhash common.Hash) *types.Receipt {
|
|
||||||
return core.GetReceipt(self.eth.ChainDb(), txhash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ethApi) StorageAt(addr, storageAddr string) string {
|
|
||||||
return self.state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ethApi) CodeAt(address string) string {
|
|
||||||
return common.ToHex(self.state.GetCode(common.HexToAddress(address)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ethApi) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
|
|
||||||
statedb := self.state.Copy()
|
|
||||||
var from *state.StateObject
|
|
||||||
if len(fromStr) == 0 {
|
|
||||||
accounts, err := self.eth.AccountManager().Accounts()
|
|
||||||
if err != nil || len(accounts) == 0 {
|
|
||||||
from = statedb.GetOrNewStateObject(common.Address{})
|
|
||||||
} else {
|
|
||||||
from = statedb.GetOrNewStateObject(accounts[0].Address)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr))
|
|
||||||
}
|
|
||||||
|
|
||||||
from.SetBalance(common.MaxBig)
|
|
||||||
|
|
||||||
msg := callmsg{
|
|
||||||
from: from,
|
|
||||||
gas: common.Big(gasStr),
|
|
||||||
gasPrice: common.Big(gasPriceStr),
|
|
||||||
value: common.Big(valueStr),
|
|
||||||
data: common.FromHex(dataStr),
|
|
||||||
}
|
|
||||||
if len(toStr) > 0 {
|
|
||||||
addr := common.HexToAddress(toStr)
|
|
||||||
msg.to = &addr
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.gas.Cmp(big.NewInt(0)) == 0 {
|
|
||||||
msg.gas = big.NewInt(50000000)
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.gasPrice.Cmp(big.NewInt(0)) == 0 {
|
|
||||||
msg.gasPrice = self.DefaultGasPrice()
|
|
||||||
}
|
|
||||||
|
|
||||||
header := self.CurrentBlock().Header()
|
|
||||||
vmenv := core.NewEnv(statedb, self.eth.BlockChain(), msg, header)
|
|
||||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
|
||||||
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
|
|
||||||
return common.ToHex(res), gas.String(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ethApi) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
|
||||||
|
|
||||||
if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) {
|
|
||||||
return "", errors.New("Invalid address")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
from = common.HexToAddress(fromStr)
|
|
||||||
to = common.HexToAddress(toStr)
|
|
||||||
value = common.Big(valueStr)
|
|
||||||
gas *big.Int
|
|
||||||
price *big.Int
|
|
||||||
data []byte
|
|
||||||
contractCreation bool
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(gasStr) == 0 {
|
|
||||||
gas = DefaultGas()
|
|
||||||
} else {
|
|
||||||
gas = common.Big(gasStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(gasPriceStr) == 0 {
|
|
||||||
price = self.DefaultGasPrice()
|
|
||||||
} else {
|
|
||||||
price = common.Big(gasPriceStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
data = common.FromHex(codeStr)
|
|
||||||
if len(toStr) == 0 {
|
|
||||||
contractCreation = true
|
|
||||||
}
|
|
||||||
|
|
||||||
self.transactMu.Lock()
|
|
||||||
defer self.transactMu.Unlock()
|
|
||||||
|
|
||||||
var nonce uint64
|
|
||||||
if len(nonceStr) != 0 {
|
|
||||||
nonce = common.Big(nonceStr).Uint64()
|
|
||||||
} else {
|
|
||||||
state := self.eth.TxPool().State()
|
|
||||||
nonce = state.GetNonce(from)
|
|
||||||
}
|
|
||||||
var tx *types.Transaction
|
|
||||||
if contractCreation {
|
|
||||||
tx = types.NewContractCreation(nonce, value, gas, price, data)
|
|
||||||
} else {
|
|
||||||
tx = types.NewTransaction(nonce, to, value, gas, price, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
signed, err := self.sign(tx, from, false)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err = self.eth.TxPool().Add(signed); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if contractCreation {
|
|
||||||
addr := crypto.CreateAddress(from, nonce)
|
|
||||||
glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signed.Hash().Hex(), addr.Hex())
|
|
||||||
} else {
|
|
||||||
glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signed.Hash().Hex(), tx.To().Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
return signed.Hash().Hex(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ethApi) sign(tx *types.Transaction, from common.Address, didUnlock bool) (*types.Transaction, error) {
|
|
||||||
hash := tx.SigHash()
|
|
||||||
sig, err := self.doSign(from, hash, didUnlock)
|
|
||||||
if err != nil {
|
|
||||||
return tx, err
|
|
||||||
}
|
|
||||||
return tx.WithSignature(sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ethApi) doSign(from common.Address, hash common.Hash, didUnlock bool) ([]byte, error) {
|
|
||||||
sig, err := self.eth.AccountManager().Sign(accounts.Account{Address: from}, hash.Bytes())
|
|
||||||
if err == accounts.ErrLocked {
|
|
||||||
if didUnlock {
|
|
||||||
return nil, fmt.Errorf("signer account still locked after successful unlock")
|
|
||||||
}
|
|
||||||
// retry signing, the account should now be unlocked.
|
|
||||||
return self.doSign(from, hash, true)
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return sig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultGas() *big.Int { return new(big.Int).Set(defaultGas) }
|
|
||||||
|
|
||||||
func (self *ethApi) DefaultGasPrice() *big.Int {
|
|
||||||
return self.gpo.SuggestPrice()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ethApi) CurrentBlock() *types.Block {
|
|
||||||
return self.eth.BlockChain().CurrentBlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ethApi) getBlockByHeight(height int64) *types.Block {
|
|
||||||
var num uint64
|
|
||||||
|
|
||||||
switch height {
|
|
||||||
case -2:
|
|
||||||
return self.eth.Miner().PendingBlock()
|
|
||||||
case -1:
|
|
||||||
return self.CurrentBlock()
|
|
||||||
default:
|
|
||||||
if height < 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
num = uint64(height)
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.eth.BlockChain().GetBlockByNumber(num)
|
|
||||||
}
|
|
||||||
|
|
||||||
// callmsg is the message type used for call transations.
|
|
||||||
type callmsg struct {
|
|
||||||
from *state.StateObject
|
|
||||||
to *common.Address
|
|
||||||
gas, gasPrice *big.Int
|
|
||||||
value *big.Int
|
|
||||||
data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAddress(addr string) bool {
|
|
||||||
return addrReg.MatchString(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// accessor boilerplate to implement core.Message
|
|
||||||
func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil }
|
|
||||||
func (m callmsg) Nonce() uint64 { return m.from.Nonce() }
|
|
||||||
func (m callmsg) To() *common.Address { return m.to }
|
|
||||||
func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
|
|
||||||
func (m callmsg) Gas() *big.Int { return m.gas }
|
|
||||||
func (m callmsg) Value() *big.Int { return m.value }
|
|
||||||
func (m callmsg) Data() []byte { return m.data }
|
|
||||||
257
swarm/api/filesystem.go
Normal file
257
swarm/api/filesystem.go
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxParallelFiles = 5
|
||||||
|
|
||||||
|
type FileSystem struct {
|
||||||
|
api *Api
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileSystem(api *Api) *FileSystem {
|
||||||
|
return &FileSystem{api}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload replicates a local directory as a manifest file and uploads it
|
||||||
|
// using dpa store
|
||||||
|
// TODO: localpath should point to a manifest
|
||||||
|
func (self *FileSystem) Upload(lpath, index string) (string, error) {
|
||||||
|
var list []*manifestTrieEntry
|
||||||
|
localpath, err := filepath.Abs(filepath.Clean(lpath))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(localpath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
stat, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var start int
|
||||||
|
if stat.IsDir() {
|
||||||
|
start = len(localpath)
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] uploading '%s'", localpath)
|
||||||
|
err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if (err == nil) && !info.IsDir() {
|
||||||
|
//fmt.Printf("lp %s path %s\n", localpath, path)
|
||||||
|
if len(path) <= start {
|
||||||
|
return fmt.Errorf("Path is too short")
|
||||||
|
}
|
||||||
|
if path[:start] != localpath {
|
||||||
|
return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath)
|
||||||
|
}
|
||||||
|
entry := &manifestTrieEntry{
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
list = append(list, entry)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dir := filepath.Dir(localpath)
|
||||||
|
start = len(dir)
|
||||||
|
if len(localpath) <= start {
|
||||||
|
return "", fmt.Errorf("Path is too short")
|
||||||
|
}
|
||||||
|
if localpath[:start] != dir {
|
||||||
|
return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir)
|
||||||
|
}
|
||||||
|
entry := &manifestTrieEntry{
|
||||||
|
Path: localpath,
|
||||||
|
}
|
||||||
|
list = append(list, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt := len(list)
|
||||||
|
errors := make([]error, cnt)
|
||||||
|
done := make(chan bool, maxParallelFiles)
|
||||||
|
dcnt := 0
|
||||||
|
|
||||||
|
for i, entry := range list {
|
||||||
|
if i >= dcnt+maxParallelFiles {
|
||||||
|
<-done
|
||||||
|
dcnt++
|
||||||
|
}
|
||||||
|
go func(i int, entry *manifestTrieEntry, done chan bool) {
|
||||||
|
f, err := os.Open(entry.Path)
|
||||||
|
if err == nil {
|
||||||
|
stat, _ := f.Stat()
|
||||||
|
sr := io.NewSectionReader(f, 0, stat.Size())
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
var hash storage.Key
|
||||||
|
hash, err = self.api.dpa.Store(sr, wg)
|
||||||
|
if hash != nil {
|
||||||
|
list[i].Hash = hash.String()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
if err == nil {
|
||||||
|
first512 := make([]byte, 512)
|
||||||
|
fread, _ := sr.ReadAt(first512, 0)
|
||||||
|
if fread > 0 {
|
||||||
|
mimeType := http.DetectContentType(first512[:fread])
|
||||||
|
if filepath.Ext(entry.Path) == ".css" {
|
||||||
|
mimeType = "text/css"
|
||||||
|
}
|
||||||
|
list[i].ContentType = mimeType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
errors[i] = err
|
||||||
|
done <- true
|
||||||
|
}(i, entry, done)
|
||||||
|
}
|
||||||
|
for dcnt < cnt {
|
||||||
|
<-done
|
||||||
|
dcnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
trie := &manifestTrie{
|
||||||
|
dpa: self.api.dpa,
|
||||||
|
}
|
||||||
|
for i, entry := range list {
|
||||||
|
if errors[i] != nil {
|
||||||
|
return "", errors[i]
|
||||||
|
}
|
||||||
|
entry.Path = RegularSlashes(entry.Path[start:])
|
||||||
|
if entry.Path == index {
|
||||||
|
ientry := &manifestTrieEntry{
|
||||||
|
Path: "",
|
||||||
|
Hash: entry.Hash,
|
||||||
|
ContentType: entry.ContentType,
|
||||||
|
}
|
||||||
|
trie.addEntry(ientry)
|
||||||
|
}
|
||||||
|
trie.addEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
err2 := trie.recalcAndStore()
|
||||||
|
var hs string
|
||||||
|
if err2 == nil {
|
||||||
|
hs = trie.hash.String()
|
||||||
|
}
|
||||||
|
return hs, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download replicates the manifest path structure on the local filesystem
|
||||||
|
// under localpath
|
||||||
|
func (self *FileSystem) Download(bzzpath, localpath string) error {
|
||||||
|
lpath, err := filepath.Abs(filepath.Clean(localpath))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(lpath, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//resolving host and port
|
||||||
|
key, _, path, err := self.api.parseAndResolve(bzzpath, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// if len(path) > 0 {
|
||||||
|
// path += "/"
|
||||||
|
// }
|
||||||
|
|
||||||
|
trie, err := loadManifest(self.api.dpa, key)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(logger.Warn).Infof("[BZZ] fs.Download: loadManifestTrie error: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type downloadListEntry struct {
|
||||||
|
key storage.Key
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
var list []*downloadListEntry
|
||||||
|
var mde, mderr error
|
||||||
|
|
||||||
|
prevPath := lpath
|
||||||
|
err = trie.listWithPrefix(path, func(entry *manifestTrieEntry, suffix string) { // TODO: paralellize
|
||||||
|
glog.V(logger.Detail).Infof("[BZZ] fs.Download: %#v", entry)
|
||||||
|
|
||||||
|
key := common.Hex2Bytes(entry.Hash)
|
||||||
|
path := lpath + "/" + suffix
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
if dir != prevPath {
|
||||||
|
mde = os.MkdirAll(dir, os.ModePerm)
|
||||||
|
if mde != nil {
|
||||||
|
mderr = mde
|
||||||
|
}
|
||||||
|
prevPath = dir
|
||||||
|
}
|
||||||
|
if (mde == nil) && (path != dir+"/") {
|
||||||
|
list = append(list, &downloadListEntry{key: key, path: path})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
err = mderr
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt := len(list)
|
||||||
|
errors := make([]error, cnt)
|
||||||
|
done := make(chan bool, maxParallelFiles)
|
||||||
|
dcnt := 0
|
||||||
|
|
||||||
|
for i, entry := range list {
|
||||||
|
if i >= dcnt+maxParallelFiles {
|
||||||
|
<-done
|
||||||
|
dcnt++
|
||||||
|
}
|
||||||
|
go func(i int, entry *downloadListEntry, done chan bool) {
|
||||||
|
f, err := os.Create(entry.path) // TODO: path separators
|
||||||
|
if err == nil {
|
||||||
|
reader := self.api.dpa.Retrieve(entry.key)
|
||||||
|
writer := bufio.NewWriter(f)
|
||||||
|
_, err = io.CopyN(writer, reader, reader.Size()) // TODO: handle errors
|
||||||
|
err2 := writer.Flush()
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
err2 = f.Close()
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errors[i] = err
|
||||||
|
done <- true
|
||||||
|
}(i, entry, done)
|
||||||
|
}
|
||||||
|
for dcnt < cnt {
|
||||||
|
<-done
|
||||||
|
dcnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i, _ := range list {
|
||||||
|
if errors[i] != nil {
|
||||||
|
return errors[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
176
swarm/api/filesystem_test.go
Normal file
176
swarm/api/filesystem_test.go
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testDir string
|
||||||
|
testDownloadDir string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_, filename, _, _ := runtime.Caller(1)
|
||||||
|
testDir = path.Join(path.Dir(filename), "../test")
|
||||||
|
testDownloadDir, _ = ioutil.TempDir(os.TempDir(), "bzz-test")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFileSystem(t *testing.T, f func(*FileSystem)) {
|
||||||
|
testApi(t, func(api *Api) {
|
||||||
|
f(NewFileSystem(api))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPath(t *testing.T, parts ...string) string {
|
||||||
|
// func readPath(t *testing.T, parts ...string) []byte {
|
||||||
|
file := path.Join(parts...)
|
||||||
|
content, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error reading '%v': %v", file, err)
|
||||||
|
}
|
||||||
|
return string(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiDirUpload0(t *testing.T) {
|
||||||
|
// t.Skip("FIXME")
|
||||||
|
testFileSystem(t, func(fs *FileSystem) {
|
||||||
|
api := fs.api
|
||||||
|
bzzhash, err := fs.Upload(path.Join(testDir, "test0"), "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content := readPath(t, testDir, "test0", "index.html")
|
||||||
|
resp := testGet(t, api, bzzhash+"/index.html")
|
||||||
|
exp := expResponse(content, "text/html; charset=utf-8", 0)
|
||||||
|
checkResponse(t, resp, exp)
|
||||||
|
|
||||||
|
content = readPath(t, testDir, "test0", "index.css")
|
||||||
|
resp = testGet(t, api, bzzhash+"/index.css")
|
||||||
|
exp = expResponse(content, "text/css", 0)
|
||||||
|
checkResponse(t, resp, exp)
|
||||||
|
|
||||||
|
content = readPath(t, testDir, "test0", "img", "logo.png")
|
||||||
|
resp = testGet(t, api, bzzhash+"/img/logo.png")
|
||||||
|
exp = expResponse(content, "image/png", 0)
|
||||||
|
|
||||||
|
_, _, _, err = api.Get(bzzhash, true)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadDir := path.Join(testDownloadDir, "test0")
|
||||||
|
os.RemoveAll(downloadDir)
|
||||||
|
defer os.RemoveAll(downloadDir)
|
||||||
|
err = fs.Download(bzzhash, downloadDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
newbzzhash, err := fs.Upload(downloadDir, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if bzzhash != newbzzhash {
|
||||||
|
t.Fatalf("download %v reuploaded has incorrect hash, expected %v, got %v", downloadDir, bzzhash, newbzzhash)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiDirUploadModify(t *testing.T) {
|
||||||
|
// t.Skip("FIXME")
|
||||||
|
testFileSystem(t, func(fs *FileSystem) {
|
||||||
|
api := fs.api
|
||||||
|
bzzhash, err := fs.Upload(path.Join(testDir, "test0"), "")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bzzhash, err = api.Modify(bzzhash+"/index.html", "", "", true)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bzzhash, err = api.Modify(bzzhash+"/index2.html", "9ea1f60ebd80786d6005f6b256376bdb494a82496cd86fe8c307cdfb23c99e71", "text/html; charset=utf-8", true)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bzzhash, err = api.Modify(bzzhash+"/img/logo.png", "9ea1f60ebd80786d6005f6b256376bdb494a82496cd86fe8c307cdfb23c99e71", "text/html; charset=utf-8", true)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := readPath(t, testDir, "test0", "index.html")
|
||||||
|
resp := testGet(t, api, bzzhash+"/index2.html")
|
||||||
|
exp := expResponse(content, "text/html; charset=utf-8", 0)
|
||||||
|
checkResponse(t, resp, exp)
|
||||||
|
|
||||||
|
resp = testGet(t, api, bzzhash+"/img/logo.png")
|
||||||
|
exp = expResponse(content, "text/html; charset=utf-8", 0)
|
||||||
|
checkResponse(t, resp, exp)
|
||||||
|
|
||||||
|
content = readPath(t, testDir, "test0", "index.css")
|
||||||
|
resp = testGet(t, api, bzzhash+"/index.css")
|
||||||
|
exp = expResponse(content, "text/css", 0)
|
||||||
|
|
||||||
|
_, _, _, err = api.Get(bzzhash, true)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiDirUploadWithRootFile(t *testing.T) {
|
||||||
|
testFileSystem(t, func(fs *FileSystem) {
|
||||||
|
api := fs.api
|
||||||
|
bzzhash, err := fs.Upload(path.Join(testDir, "test0"), "index.html")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := readPath(t, testDir, "test0", "index.html")
|
||||||
|
resp := testGet(t, api, bzzhash)
|
||||||
|
exp := expResponse(content, "text/html; charset=utf-8", 0)
|
||||||
|
checkResponse(t, resp, exp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiFileUpload(t *testing.T) {
|
||||||
|
testFileSystem(t, func(fs *FileSystem) {
|
||||||
|
api := fs.api
|
||||||
|
bzzhash, err := fs.Upload(path.Join(testDir, "test0", "index.html"), "")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := readPath(t, testDir, "test0", "index.html")
|
||||||
|
resp := testGet(t, api, bzzhash+"/index.html")
|
||||||
|
exp := expResponse(content, "text/html; charset=utf-8", 0)
|
||||||
|
checkResponse(t, resp, exp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiFileUploadWithRootFile(t *testing.T) {
|
||||||
|
testFileSystem(t, func(fs *FileSystem) {
|
||||||
|
api := fs.api
|
||||||
|
bzzhash, err := fs.Upload(path.Join(testDir, "test0", "index.html"), "index.html")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := readPath(t, testDir, "test0", "index.html")
|
||||||
|
resp := testGet(t, api, bzzhash)
|
||||||
|
exp := expResponse(content, "text/html; charset=utf-8", 0)
|
||||||
|
checkResponse(t, resp, exp)
|
||||||
|
})
|
||||||
|
}
|
||||||
53
swarm/api/http/roundtripper.go
Normal file
53
swarm/api/http/roundtripper.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
http roundtripper to register for bzz url scheme
|
||||||
|
see https://github.com/ethereum/go-ethereum/issues/2040
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/api/http"
|
||||||
|
)
|
||||||
|
client := httpclient.New()
|
||||||
|
// for (private) swarm proxy running locally
|
||||||
|
client.RegisterScheme("bzz", &http.RoundTripper{Port: port})
|
||||||
|
client.RegisterScheme("bzzi", &http.RoundTripper{Port: port})
|
||||||
|
client.RegisterScheme("bzzr", &http.RoundTripper{Port: port})
|
||||||
|
|
||||||
|
The port you give the Roundtripper is the port the swarm proxy is listening on.
|
||||||
|
If Host is left empty, localhost is assumed.
|
||||||
|
|
||||||
|
Using a public gateway, the above few lines gives you the leanest
|
||||||
|
bzz-scheme aware read-only http client. You really only ever need this
|
||||||
|
if you need go-native swarm access to bzz addresses, e.g.,
|
||||||
|
github.com/ethereum/go-ethereum/common/natspec
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
type RoundTripper struct {
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *RoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||||
|
host := self.Host
|
||||||
|
if len(host) == 0 {
|
||||||
|
host = "localhost"
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf("http://%s:%s/%s:/%s/%s", host, self.Port, req.Proto, req.URL.Host, req.URL.Path)
|
||||||
|
glog.V(logger.Info).Infof("[BZZ] roundtripper: proxying request '%s' to '%s'", req.RequestURI, url)
|
||||||
|
reqProxy, err := http.NewRequest(req.Method, url, req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return http.DefaultClient.Do(reqProxy)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package api
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -22,7 +22,7 @@ func TestRoundTripper(t *testing.T) {
|
||||||
})
|
})
|
||||||
go http.ListenAndServe(":8600", serveMux)
|
go http.ListenAndServe(":8600", serveMux)
|
||||||
|
|
||||||
rt := &RoundTripper{"8600"}
|
rt := &RoundTripper{Port: "8600"}
|
||||||
client := httpclient.New("/")
|
client := httpclient.New("/")
|
||||||
client.RegisterProtocol("bzz", rt)
|
client.RegisterProtocol("bzz", rt)
|
||||||
|
|
||||||
|
|
@ -43,8 +43,8 @@ func TestRoundTripper(t *testing.T) {
|
||||||
t.Errorf("expected no error, got %v", err)
|
t.Errorf("expected no error, got %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if string(content) != "/test.com/path" {
|
if string(content) != "/HTTP/1.1:/test.com/path" {
|
||||||
t.Errorf("incorrect response from http server: expected '%v', got '%v'", "/test.com/path", string(content))
|
t.Errorf("incorrect response from http server: expected '%v', got '%v'", "/HTTP/1.1:/test.com/path", string(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
A simple http server interface to Swarm
|
A simple http server interface to Swarm
|
||||||
*/
|
*/
|
||||||
package api
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -21,8 +22,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bzzPrefix = regexp.MustCompile("^/+bzz:/+")
|
// accepted protocols: bzz (traditional), bzzi (immutable) and bzzr (raw)
|
||||||
rawUrl = regexp.MustCompile("^/+raw/*")
|
bzzPrefix = regexp.MustCompile("^/+bzz[ir]?:/+")
|
||||||
trailingSlashes = regexp.MustCompile("/+$")
|
trailingSlashes = regexp.MustCompile("/+$")
|
||||||
// forever = func() time.Time { return time.Unix(0, 0) }
|
// forever = func() time.Time { return time.Unix(0, 0) }
|
||||||
forever = time.Now
|
forever = time.Now
|
||||||
|
|
@ -41,7 +42,7 @@ type sequentialReader struct {
|
||||||
// https://github.com/atom/electron/blob/master/docs/api/protocol.md
|
// https://github.com/atom/electron/blob/master/docs/api/protocol.md
|
||||||
|
|
||||||
// starts up http server
|
// starts up http server
|
||||||
func StartHttpServer(api *Api, port string) {
|
func StartHttpServer(api *api.Api, port string) {
|
||||||
serveMux := http.NewServeMux()
|
serveMux := http.NewServeMux()
|
||||||
serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
handler(w, r, api)
|
handler(w, r, api)
|
||||||
|
|
@ -50,7 +51,7 @@ func StartHttpServer(api *Api, port string) {
|
||||||
glog.V(logger.Info).Infof("[BZZ] Swarm HTTP proxy started on localhost:%s", port)
|
glog.V(logger.Info).Infof("[BZZ] Swarm HTTP proxy started on localhost:%s", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
func handler(w http.ResponseWriter, r *http.Request, a *api.Api) {
|
||||||
requestURL := r.URL
|
requestURL := r.URL
|
||||||
// This is wrong
|
// This is wrong
|
||||||
// if requestURL.Host == "" {
|
// if requestURL.Host == "" {
|
||||||
|
|
@ -61,24 +62,41 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: HTTP request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept"))
|
glog.V(logger.Debug).Infof("[BZZ] Swarm: HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept"))
|
||||||
uri := requestURL.Path
|
uri := requestURL.Path
|
||||||
var raw bool
|
var raw, nameresolver bool
|
||||||
|
var proto string
|
||||||
|
|
||||||
// HTTP-based URL protocol handler
|
// HTTP-based URL protocol handler
|
||||||
uri = bzzPrefix.ReplaceAllString(uri, "")
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: BZZ request URI: '%s'", uri)
|
glog.V(logger.Debug).Infof("[BZZ] Swarm: BZZ request URI: '%s'", uri)
|
||||||
|
|
||||||
path := rawUrl.ReplaceAllStringFunc(uri, func(string) string {
|
path := bzzPrefix.ReplaceAllStringFunc(uri, func(p string) string {
|
||||||
raw = true
|
proto = p
|
||||||
return ""
|
return ""
|
||||||
})
|
})
|
||||||
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: %s request '%s' received.", r.Method, uri)
|
// protocol identification (ugly)
|
||||||
|
if proto == "" {
|
||||||
|
if glog.V(logger.Error) {
|
||||||
|
glog.Errorf(
|
||||||
|
"[BZZ] Swarm: Protocol error in request `%s`.",
|
||||||
|
uri,
|
||||||
|
)
|
||||||
|
http.Error(w, "BZZ protocol error", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raw = proto[1:5] == "bzzr"
|
||||||
|
nameresolver = proto[1:5] != "bzzi"
|
||||||
|
|
||||||
|
glog.V(logger.Debug).Infof(
|
||||||
|
"[BZZ] Swarm: %s request over protocol %s '%s' received.",
|
||||||
|
r.Method, proto, path,
|
||||||
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case r.Method == "POST" || r.Method == "PUT":
|
case r.Method == "POST" || r.Method == "PUT":
|
||||||
key, err := api.dpa.Store(io.NewSectionReader(&sequentialReader{
|
key, err := a.Store(io.NewSectionReader(&sequentialReader{
|
||||||
reader: r.Body,
|
reader: r.Body,
|
||||||
ahead: make(map[int64]chan bool),
|
ahead: make(map[int64]chan bool),
|
||||||
}, 0, r.ContentLength), nil)
|
}, 0, r.ContentLength), nil)
|
||||||
|
|
@ -102,11 +120,11 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
http.Error(w, "No PUT to /raw allowed.", http.StatusBadRequest)
|
http.Error(w, "No PUT to /raw allowed.", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
path = regularSlashes(path)
|
path = api.RegularSlashes(path)
|
||||||
mime := r.Header.Get("Content-Type")
|
mime := r.Header.Get("Content-Type")
|
||||||
// TODO proper root hash separation
|
// TODO proper root hash separation
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Modify '%s' to store %v as '%s'.", path, key.Log(), mime)
|
glog.V(logger.Debug).Infof("[BZZ] Modify '%s' to store %v as '%s'.", path, key.Log(), mime)
|
||||||
newKey, err := api.Modify(path[:64], path[65:], common.Bytes2Hex(key), mime)
|
newKey, err := a.Modify(path, common.Bytes2Hex(key), mime, nameresolver)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm replaced manifest by '%s'", newKey)
|
glog.V(logger.Debug).Infof("[BZZ] Swarm replaced manifest by '%s'", newKey)
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
@ -122,9 +140,9 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
http.Error(w, "No DELETE to /raw allowed.", http.StatusBadRequest)
|
http.Error(w, "No DELETE to /raw allowed.", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
path = regularSlashes(path)
|
path = api.RegularSlashes(path)
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Delete '%s'.", path)
|
glog.V(logger.Debug).Infof("[BZZ] Delete '%s'.", path)
|
||||||
newKey, err := api.Modify(path[:64], path[65:], "", "")
|
newKey, err := a.Modify(path, "", "", nameresolver)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm replaced manifest by '%s'", newKey)
|
glog.V(logger.Debug).Infof("[BZZ] Swarm replaced manifest by '%s'", newKey)
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
@ -138,7 +156,7 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
path = trailingSlashes.ReplaceAllString(path, "")
|
path = trailingSlashes.ReplaceAllString(path, "")
|
||||||
if raw {
|
if raw {
|
||||||
// resolving host
|
// resolving host
|
||||||
key, err := api.Resolve(path)
|
key, err := a.Resolve(path, nameresolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Error).Infof("[BZZ] Swarm: %v", err)
|
glog.V(logger.Error).Infof("[BZZ] Swarm: %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
|
@ -146,7 +164,7 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieving content
|
// retrieving content
|
||||||
reader := api.dpa.Retrieve(key)
|
reader := a.Retrieve(key)
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: Reading %d bytes.", reader.Size())
|
glog.V(logger.Debug).Infof("[BZZ] Swarm: Reading %d bytes.", reader.Size())
|
||||||
|
|
||||||
// setting mime type
|
// setting mime type
|
||||||
|
|
@ -165,10 +183,9 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: Structured GET request '%s' received.", uri)
|
glog.V(logger.Debug).Infof("[BZZ] Swarm: Structured GET request '%s' received.", uri)
|
||||||
|
|
||||||
// call to api.getPath on uri
|
reader, mimeType, status, err := a.Get(path, nameresolver)
|
||||||
reader, mimeType, status, err := api.getPath(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(errResolve); ok {
|
if _, ok := err.(api.ErrResolve); ok {
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: %v", err)
|
glog.V(logger.Debug).Infof("[BZZ] Swarm: %v", err)
|
||||||
status = http.StatusBadRequest
|
status = http.StatusBadRequest
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -145,7 +145,10 @@ func (self *manifestTrie) deleteEntry(path string) {
|
||||||
|
|
||||||
b := byte(path[0])
|
b := byte(path[0])
|
||||||
entry := self.entries[b]
|
entry := self.entries[b]
|
||||||
if (entry != nil) && (entry.Path == path) {
|
if entry == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if entry.Path == path {
|
||||||
self.entries[b] = nil
|
self.entries[b] = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +293,7 @@ func (self *manifestTrie) findPrefixOf(path string) (entry *manifestTrieEntry, p
|
||||||
|
|
||||||
// file system manifest always contains regularized paths
|
// file system manifest always contains regularized paths
|
||||||
// no leading or trailing slashes, only single slashes inside
|
// no leading or trailing slashes, only single slashes inside
|
||||||
func regularSlashes(path string) (res string) {
|
func RegularSlashes(path string) (res string) {
|
||||||
for i := 0; i < len(path); i++ {
|
for i := 0; i < len(path); i++ {
|
||||||
if (path[i] != '/') || ((i > 0) && (path[i-1] != '/')) {
|
if (path[i] != '/') || ((i > 0) && (path[i-1] != '/')) {
|
||||||
res = res + path[i:i+1]
|
res = res + path[i:i+1]
|
||||||
|
|
@ -303,7 +306,7 @@ func regularSlashes(path string) (res string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *manifestTrie) getEntry(spath string) (entry *manifestTrieEntry, fullpath string) {
|
func (self *manifestTrie) getEntry(spath string) (entry *manifestTrieEntry, fullpath string) {
|
||||||
path := regularSlashes(spath)
|
path := RegularSlashes(spath)
|
||||||
var pos int
|
var pos int
|
||||||
entry, pos = self.findPrefixOf(path)
|
entry, pos = self.findPrefixOf(path)
|
||||||
return entry, path[:pos]
|
return entry, path[:pos]
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
|
|
||||||
// "github.com/ethereum/go-ethereum/common/httpclient"
|
|
||||||
// "github.com/ethereum/go-ethereum/jsre"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RoundTripper struct {
|
|
||||||
Port string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *RoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
|
||||||
url := fmt.Sprintf("http://localhost:%s/%s/%s", self.Port, req.URL.Host, req.URL.Path)
|
|
||||||
glog.V(logger.Info).Infof("[BZZ] roundtripper: proxying request '%s' to '%s'", req.RequestURI, url)
|
|
||||||
return http.Get(url)
|
|
||||||
}
|
|
||||||
48
swarm/api/storage.go
Normal file
48
swarm/api/storage.go
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
MimeType string
|
||||||
|
Status int
|
||||||
|
Size int64
|
||||||
|
// Content []byte
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
// implements a service
|
||||||
|
type Storage struct {
|
||||||
|
api *Api
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStorage(api *Api) *Storage {
|
||||||
|
return &Storage{api}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put uploads the content to the swarm with a simple manifest speficying
|
||||||
|
// its content type
|
||||||
|
func (self *Storage) Put(content, contentType string) (string, error) {
|
||||||
|
return self.api.Put(content, contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the content from bzzpath and reads the response in full
|
||||||
|
// It returns the Response object, which serialises containing the
|
||||||
|
// response body as the value of the Content field
|
||||||
|
// NOTE: if error is non-nil, sResponse may still have partial content
|
||||||
|
// the actual size of which is given in len(resp.Content), while the expected
|
||||||
|
// size is resp.Size
|
||||||
|
func (self *Storage) Get(bzzpath string) (*Response, error) {
|
||||||
|
reader, mimeType, status, err := self.api.Get(bzzpath, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
expsize := reader.Size()
|
||||||
|
body := make([]byte, expsize)
|
||||||
|
size, err := reader.Read(body)
|
||||||
|
if int64(size) == expsize {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return &Response{mimeType, status, expsize, string(body[:size])}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Storage) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) {
|
||||||
|
return self.api.Modify(rootHash+"/"+path, contentHash, contentType, true)
|
||||||
|
}
|
||||||
33
swarm/api/storage_test.go
Normal file
33
swarm/api/storage_test.go
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testStorage(t *testing.T, f func(*Storage)) {
|
||||||
|
testApi(t, func(api *Api) {
|
||||||
|
f(NewStorage(api))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoragePutGet(t *testing.T) {
|
||||||
|
testStorage(t, func(api *Storage) {
|
||||||
|
content := "hello"
|
||||||
|
exp := expResponse(content, "text/plain", 0)
|
||||||
|
// exp := expResponse([]byte(content), "text/plain", 0)
|
||||||
|
bzzhash, err := api.Put(content, exp.MimeType)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
// to check put against the Api#Get
|
||||||
|
resp0 := testGet(t, api.api, bzzhash)
|
||||||
|
checkResponse(t, resp0, exp)
|
||||||
|
|
||||||
|
// check storage#Get
|
||||||
|
resp, err := api.Get(bzzhash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
checkResponse(t, &testResponse{nil, resp}, exp)
|
||||||
|
})
|
||||||
|
}
|
||||||
30
swarm/api/testapi.go
Normal file
30
swarm/api/testapi.go
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Control struct {
|
||||||
|
api *Api
|
||||||
|
hive *network.Hive
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewControl(api *Api, hive *network.Hive) *Control {
|
||||||
|
return &Control{api, hive}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Control) BlockNetworkRead(on bool) {
|
||||||
|
self.hive.BlockNetworkRead(on)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Control) SyncEnabled(on bool) {
|
||||||
|
self.hive.SyncEnabled(on)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Control) SwapEnabled(on bool) {
|
||||||
|
self.hive.SwapEnabled(on)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Control) Hive() string {
|
||||||
|
return self.hive.String()
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
|
|
||||||
INDEX='index.html'
|
INDEX='index.html'
|
||||||
port="8500"
|
proxy="http://localhost:8500"
|
||||||
delimiter='{"entries":[{'
|
delimiter='{"entries":[{'
|
||||||
|
|
||||||
|
|
||||||
if [[ ! -z "$2" ]]; then
|
if [[ ! -z "$2" ]]; then
|
||||||
port="$2"
|
proxy="$2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f "$1" ]; then
|
if [ -f "$1" ]; then
|
||||||
hash=`wget -q -O- --post-file="$1" http://localhost:$port/raw`
|
hash=`wget -q -O- --post-file="$1" $proxy/bzzr:/`
|
||||||
mime=`mimetype -b "$1"`
|
mime=`mimetype -b "$1"`
|
||||||
wget -q -O- --post-data="$delimiter\"hash\":\"$hash\",\"contentType\":\"$mime\"}]}" http://localhost:8500/raw
|
# echo wget -q -O- --post-data="$delimiter\"hash\":\"$hash\",\"contentType\":\"$mime\"}]}" $proxy/bzzr:/
|
||||||
|
wget -q -O- --post-data="$delimiter\"hash\":\"$hash\",\"contentType\":\"$mime\"}]}" $proxy/bzzr:/
|
||||||
echo
|
echo
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
@ -28,8 +30,11 @@ do
|
||||||
name=`echo "$path" | cut -c3-`
|
name=`echo "$path" | cut -c3-`
|
||||||
[ _`basename "$name"` = "_$INDEX" ] && name=`dirname "$name"`
|
[ _`basename "$name"` = "_$INDEX" ] && name=`dirname "$name"`
|
||||||
echo -n "$delimiter"
|
echo -n "$delimiter"
|
||||||
hash=`wget -q -O- --post-file="$path" http://localhost:$port/raw`
|
hash=`wget -q -O- --post-file="$path" $proxy/bzzr:/`
|
||||||
mime=`mimetype -b "$path"`
|
mime=`mimetype -b "$path"`
|
||||||
|
if [ "$mime" = "text/plain" ]; then
|
||||||
|
echo -n $path|grep -q '.css' && mime="text/css"
|
||||||
|
fi
|
||||||
if [ "_$name" = '_.' ]; then
|
if [ "_$name" = '_.' ]; then
|
||||||
echo -n "\"hash\":\"$hash\",\"contentType\":\"$mime\""
|
echo -n "\"hash\":\"$hash\",\"contentType\":\"$mime\""
|
||||||
else
|
else
|
||||||
|
|
@ -38,7 +43,7 @@ fi
|
||||||
delimiter='},{'
|
delimiter='},{'
|
||||||
|
|
||||||
done
|
done
|
||||||
echo -n '}]}') | wget -q -O- --post-data=`cat` http://localhost:$port/raw
|
echo -n '}]}') | wget -q -O- --post-data=`cat` $proxy/bzzr:/
|
||||||
echo
|
echo
|
||||||
|
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
|
||||||
149
swarm/cmd/swarm/gethup.sh
Normal file
149
swarm/cmd/swarm/gethup.sh
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Usage:
|
||||||
|
# bash /path/to/eth-utils/gethup.sh <datadir> <instance_name> <ip_addr>
|
||||||
|
|
||||||
|
root=$1 # base directory to use for datadir and logs
|
||||||
|
shift
|
||||||
|
id=$1 # double digit instance id like 00 01 02
|
||||||
|
shift
|
||||||
|
ip_addr=$1 # ip address to substitute
|
||||||
|
shift
|
||||||
|
|
||||||
|
# logs are output to a date-tagged file for each run , while a link is
|
||||||
|
# created to the latest, so that monitoring be easier with the same filename
|
||||||
|
# TODO: use this if GETH not set
|
||||||
|
# GETH=geth
|
||||||
|
# echo "ls -l $GETH"
|
||||||
|
# ls -l $GETH
|
||||||
|
|
||||||
|
# geth CLI params e.g., (dd=04, run=09)
|
||||||
|
datetag=`date "+%c%y%m%d-%H%M%S"|cut -d ' ' -f 5`
|
||||||
|
datadir=$root/data/$id # /tmp/eth/04
|
||||||
|
log=$root/log/$id.$datetag.log # /tmp/eth/04.09.log
|
||||||
|
linklog=$root/log/$id.current.log # /tmp/eth/04.09.log
|
||||||
|
stablelog=$root/log/$id.log # /tmp/eth/04.09.log
|
||||||
|
password=$id # 04
|
||||||
|
port=303$id # 34504
|
||||||
|
bzzport=322$id # 32204
|
||||||
|
rpcport=302$id # 3204
|
||||||
|
|
||||||
|
mkdir -p $root/data
|
||||||
|
mkdir -p $root/enodes
|
||||||
|
mkdir -p $root/pids
|
||||||
|
mkdir -p $root/log
|
||||||
|
ln -sf "$log" "$linklog"
|
||||||
|
# if we do not have an account, create one
|
||||||
|
# will not prompt for password, we use the double digit instance id as passwd
|
||||||
|
# NEVER EVER USE THESE ACCOUNTS FOR INTERACTING WITH A LIVE CHAIN
|
||||||
|
keystoredir="$datadir/keystore/"
|
||||||
|
# echo "KeyStore dir: $keystoredir"
|
||||||
|
if [ ! -d "$keystoredir" ]; then
|
||||||
|
# echo "create an account with password $id [DO NOT EVER USE THIS ON LIVE]"
|
||||||
|
# mkdir -p $datadir/keystore
|
||||||
|
$GETH --datadir $datadir --password <(echo -n $id) account new >/dev/null 2>&1
|
||||||
|
# create account with password 00, 01, ...
|
||||||
|
# note that the account key will be stored also separately outside
|
||||||
|
# datadir
|
||||||
|
# this way you can safely clear the data directory and still keep your key
|
||||||
|
# under `<rootdir>/keystore/dd
|
||||||
|
# LS=`ls $datadir/keystore`
|
||||||
|
# echo $LS
|
||||||
|
while [ ! -d "$keystoredir" ]; do
|
||||||
|
echo "."
|
||||||
|
((i++))
|
||||||
|
if ((i>10)); then break; fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
# echo "copying keys $datadir/keystore $root/keystore/$id"
|
||||||
|
mkdir -p $root/keystore/$id
|
||||||
|
cp -R "$datadir/keystore/" $root/keystore/$id
|
||||||
|
fi
|
||||||
|
|
||||||
|
# # mkdir -p $datadir/keystore
|
||||||
|
# if [ ! -d "$datadir/keystore" ]; then
|
||||||
|
# echo "copying keys $root/keystore/$id $datadir/keystore"
|
||||||
|
# cp -R $root/keystore/$id/keystore/ $datadir/keystore/
|
||||||
|
# fi
|
||||||
|
|
||||||
|
|
||||||
|
# query node's enode url
|
||||||
|
if [ $ip_addr="" ]; then
|
||||||
|
pattern='\d+\.\d+\.\d+\.\d+'
|
||||||
|
ip_addr="[::]"
|
||||||
|
else
|
||||||
|
pattern='\[\:\:\]'
|
||||||
|
fi
|
||||||
|
|
||||||
|
geth="$GETH --datadir $datadir --port $port"
|
||||||
|
|
||||||
|
# echo -n "enode for instance $id... "
|
||||||
|
if [ ! "$GETH" = "" ] && [ ! -f $root/enodes/$id.enode ]; then
|
||||||
|
cmd="$geth js <(echo 'console.log(admin.nodeInfo.enode); exit();') "
|
||||||
|
# echo $cmd '2>/dev/null |grep enode | perl -pe "s/'$pattern'/'$ip_addr'/g" | perl -pe "s/^/\"/; s/\s*$/\"/;" > '$root/enodes/$id.enode
|
||||||
|
eval $cmd 2>/dev/null |grep enode | perl -pe "s/$pattern/$ip_addr/g" | perl -pe "s/^/\"/; s/\s*\$/\"/;" > $root/enodes/$id.enode
|
||||||
|
fi
|
||||||
|
# cat $root/enodes/$id.enode
|
||||||
|
echo
|
||||||
|
|
||||||
|
# copy cluster enodes list to node's static node list
|
||||||
|
# echo "copy cluster enodes list to node's static node list"
|
||||||
|
if [ -f $root/enodes.all ]; then
|
||||||
|
cp $root/enodes.all $datadir/static-nodes.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f $root/pids/$id.pid ]; then
|
||||||
|
# bring up node `dd` (double digit)
|
||||||
|
# - using <rootdir>/<dd>
|
||||||
|
# - listening on port 303dd, (like 30300, 30301, ...)
|
||||||
|
# - with the account unlocked
|
||||||
|
# - launching json-rpc server on port 81dd (like 8100, 8101, 8102, ...)
|
||||||
|
# echo "BZZKEY=$geth account list|head -n1|perl -ne '/([a-f0-9]{40})/ && print \$1'"
|
||||||
|
BZZKEY=`$geth account list|head -n1|perl -ne '/([a-f0-9]{40})/ && print \$1'`
|
||||||
|
echo -n "starting instance $id ($BZZKEY @ $datadir )..."
|
||||||
|
# echo "$geth \
|
||||||
|
# --identity=$id \
|
||||||
|
# --bzzaccount=$BZZKEY --bzzport=$bzzport \
|
||||||
|
# --unlock=$BZZKEY \
|
||||||
|
# --password=<(echo -n $id) \
|
||||||
|
# --rpc --rpcport=$rpcport --rpccorsdomain='*' $* \
|
||||||
|
# 2>&1 | tee "$stablelog" > "$log" & # comment out if you pipe it to a tty etc.
|
||||||
|
# " >&2
|
||||||
|
|
||||||
|
if [ -f $log ] && [ -f $root/stablelog ]; then
|
||||||
|
cp $stablelog `cat $root/prevlog`
|
||||||
|
fi
|
||||||
|
echo $log > $root/prevlog
|
||||||
|
|
||||||
|
$GETH --datadir=$datadir \
|
||||||
|
--identity=$id \
|
||||||
|
--bzzaccount=$BZZKEY --bzzport=$bzzport \
|
||||||
|
--port=$port \
|
||||||
|
--unlock=$BZZKEY \
|
||||||
|
--password=<(echo -n $id) \
|
||||||
|
--rpc --rpcport=$rpcport --rpccorsdomain='*' $* \
|
||||||
|
> "$stablelog" 2>&1 & # comment out if you pipe it to a tty etc.
|
||||||
|
|
||||||
|
# wait until ready
|
||||||
|
# pid=`ps auxwww|grep geth|grep "ty=$id"|grep -v grep|awk '{print $2}'`
|
||||||
|
# echo "pid: $pid"
|
||||||
|
# ps auxwww|grep geth|grep "ty=$id"|grep -v grep
|
||||||
|
# echo $pid > $root/pids/$id.pid
|
||||||
|
#echo $! > $root/pids/$id.pid
|
||||||
|
while true; do
|
||||||
|
$GETH --exec="net" attach ipc:$datadir/geth.ipc > /dev/null 2>&1 && break
|
||||||
|
sleep 1
|
||||||
|
echo -n "."
|
||||||
|
if ((i++>10)); then
|
||||||
|
echo "instance $id failed to start"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo -n "started - "
|
||||||
|
pid=`ps auxwww|grep geth|grep "ty=$id"|grep -v grep|awk '{print $2}'`
|
||||||
|
echo "pid: $pid"
|
||||||
|
# ps auxwww|grep geth|grep "ty=$id"|grep -v grep
|
||||||
|
echo $pid > $root/pids/$id.pid
|
||||||
|
fi
|
||||||
|
|
||||||
|
# to bring up logs, uncomment
|
||||||
|
# tail -f $log
|
||||||
316
swarm/cmd/swarm/swarm.sh
Normal file
316
swarm/cmd/swarm/swarm.sh
Normal file
|
|
@ -0,0 +1,316 @@
|
||||||
|
# !/bin/bash
|
||||||
|
# bash cluster <root> <network_id> <number_of_nodes> <runid> <local_IP> [[params]...]
|
||||||
|
# https://github.com/ethereum/go-ethereum/wiki/Setting-up-monitoring-on-local-cluster
|
||||||
|
|
||||||
|
# sets up a local ethereum network cluster of nodes
|
||||||
|
# - <number_of_nodes> is the number of nodes in cluster
|
||||||
|
# - <root> is the root directory for the cluster, the nodes are set up
|
||||||
|
# with datadir `<root>/<network_id>/00`, `<root>/ <network_id>/01`, ...
|
||||||
|
# - new accounts are created for each node
|
||||||
|
# - they launch on port 30300, 30301, ...
|
||||||
|
# - they star rpc on port 8100, 8101, ...
|
||||||
|
# - by collecting the nodes nodeUrl, they get connected to each other
|
||||||
|
# - if enode has no IP, `<local_IP>` is substituted
|
||||||
|
# - if `<network_id>` is not 0, they will not connect to a default client,
|
||||||
|
# resulting in a private isolated network
|
||||||
|
# - the nodes log into `<root>/<network_id>/00.<runid>.log`, `<root>/<network_id>/01.<runid>.log`, ...
|
||||||
|
# - The nodes launch in mining mode
|
||||||
|
# - the cluster can be killed with `killall geth` (FIXME: should record PIDs)
|
||||||
|
# and restarted from the same state
|
||||||
|
# - if you want to interact with the nodes, use rpc
|
||||||
|
# - you can supply additional params on the command line which will be passed
|
||||||
|
# to each node, for instance `-mine`
|
||||||
|
|
||||||
|
if [ "$GETH" = "" ]; then
|
||||||
|
echo "env var GETH not set "
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
srcdir=`dirname $0`
|
||||||
|
|
||||||
|
root=$1
|
||||||
|
shift
|
||||||
|
network_id=$1
|
||||||
|
shift
|
||||||
|
cmd=$1
|
||||||
|
shift
|
||||||
|
# ip_addr=`curl ipecho.net/plain 2>/dev/null;echo `
|
||||||
|
|
||||||
|
# echo "external IP: $ip_addr"
|
||||||
|
swarmoptions='--dev --maxpeers=20 --shh=false --nodiscover'
|
||||||
|
tmpdir=/tmp
|
||||||
|
|
||||||
|
function attach {
|
||||||
|
id=$1
|
||||||
|
shift
|
||||||
|
echo "attaching console to instance $id"
|
||||||
|
cmd="$GETH $* attach ipc:$root/$network_id/data/$id/geth.ipc"
|
||||||
|
# echo $cmd
|
||||||
|
eval $cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
function log {
|
||||||
|
id=$1
|
||||||
|
shift
|
||||||
|
echo "streaming logs for instance $id"
|
||||||
|
cmd="tail -f $root/$network_id/log/$id.log"
|
||||||
|
echo $cmd
|
||||||
|
eval $cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
function less {
|
||||||
|
id=$1
|
||||||
|
shift
|
||||||
|
echo "viewing logs for instance $id"
|
||||||
|
cmd="/usr/bin/less $root/$network_id/log/$id.log"
|
||||||
|
echo $cmd
|
||||||
|
eval $cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
function start {
|
||||||
|
id=$1
|
||||||
|
shift
|
||||||
|
# echo -n "starting instance $id - "
|
||||||
|
cmd="bash $srcdir/gethup.sh $root/$network_id/ $id '$ip_addr' --networkid=$network_id $swarmoptions $*"
|
||||||
|
# echo "pid="`cat $root/$network_id/pids/$id.pid`
|
||||||
|
# echo $cmd
|
||||||
|
eval $cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop {
|
||||||
|
id=$1
|
||||||
|
shift
|
||||||
|
if [ $id = "all" ]; then
|
||||||
|
procs=`cat $root/$network_id/pids/*.pid 2>/dev/null |perl -pe 's/^\s+//;s/\s+\\$//;s/\s+/\n/g'`
|
||||||
|
# echo "stopping processes $procs"
|
||||||
|
for p in $procs; do
|
||||||
|
shutdown $p
|
||||||
|
done
|
||||||
|
rm -rf $root/$network_id/pids/*
|
||||||
|
else
|
||||||
|
pid=$root/$network_id/pids/$id.pid
|
||||||
|
if [ -f $pid ]; then
|
||||||
|
echo "stopping instance $id, pid="`cat $pid`
|
||||||
|
shutdown `cat $pid`
|
||||||
|
rm $pid
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# ps auxwww|grep geth|grep bzz|grep -v grep
|
||||||
|
}
|
||||||
|
|
||||||
|
function shutdown {
|
||||||
|
echo -n "stopping $1..."
|
||||||
|
kill -2 $1
|
||||||
|
while true ;do
|
||||||
|
ps auxwww|grep geth|grep -v grep|awk '{print $2}'|grep -ql $1 || break
|
||||||
|
sleep 1
|
||||||
|
# ps auxwww|grep geth|grep -v grep |grep $1 #|awk '{print $2}'
|
||||||
|
done
|
||||||
|
echo "stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
function restart {
|
||||||
|
id=$1
|
||||||
|
shift
|
||||||
|
stop $id
|
||||||
|
start $id $*
|
||||||
|
}
|
||||||
|
|
||||||
|
function init {
|
||||||
|
stop all
|
||||||
|
killall geth
|
||||||
|
reset all
|
||||||
|
cluster $*
|
||||||
|
stop all
|
||||||
|
cluster $*
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset {
|
||||||
|
id=$1
|
||||||
|
shift
|
||||||
|
if [ $id = "all" ]; then
|
||||||
|
rm -rf $root/$network_id
|
||||||
|
else
|
||||||
|
rm -rf$root/$network_id/*/$id*
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function cluster {
|
||||||
|
N=$1
|
||||||
|
shift
|
||||||
|
echo "launching cluster of $N instances"
|
||||||
|
# cmd="bash $srcdir/gethcluster.sh $root $network_id $N '' $swarmoptions $*"
|
||||||
|
# echo $cmd
|
||||||
|
# eval $cmd
|
||||||
|
dir=$root/$network_id
|
||||||
|
mkdir -p $dir/data
|
||||||
|
mkdir -p $dir/enodes
|
||||||
|
mkdir -p $dir/pids
|
||||||
|
mkdir -p $dir/log
|
||||||
|
|
||||||
|
enodes=$dir/enodes.all
|
||||||
|
rm -f $enodes
|
||||||
|
# build a static nodes(-like) list of all enodes of the local cluster
|
||||||
|
echo "[" >> $enodes
|
||||||
|
for ((i=0;i<N;++i)); do
|
||||||
|
id=`printf "%02d" $i`
|
||||||
|
enode=$dir/enodes/$id.enode
|
||||||
|
if [ -f "$enode" ]; then
|
||||||
|
cat "$enode" >> $enodes
|
||||||
|
echo "," >> $enodes
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "\"\"]" >> $enodes
|
||||||
|
|
||||||
|
for ((i=0;i<N;++i)); do
|
||||||
|
id=`printf "%02d" $i`
|
||||||
|
mkdir -p $dir/data/$id
|
||||||
|
echo "launching node $i/$N ---> tail -f $dir/log/$id.log"
|
||||||
|
start $id $vmodule $*
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function needs {
|
||||||
|
id=$1
|
||||||
|
keyfile=$2
|
||||||
|
target=$3
|
||||||
|
dir=`dirname $3`
|
||||||
|
dest=$tmpdir/down
|
||||||
|
mkdir -p $dest
|
||||||
|
file=$dest/`basename $target`
|
||||||
|
rm -f $file
|
||||||
|
echo -n "waiting for root hash in '$keyfile'..."
|
||||||
|
while true; do
|
||||||
|
if [ -f $keyfile ] && [ ! -z $keyfile ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
echo -n "."
|
||||||
|
done
|
||||||
|
key=`cat $keyfile|tr -d \"`
|
||||||
|
echo " => $key"
|
||||||
|
download $id $key $dest && cmp --silent $file $target && echo "PASS" || echo "FAIL"
|
||||||
|
# && ls -l $keyfile $file $target
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function up { #port, file
|
||||||
|
echo "Upload file '$2' to node $1... " 1>&2
|
||||||
|
file=`basename $2`
|
||||||
|
attach $1 "--exec 'bzz.upload(\"$2\", \"$file\")'"|tail -n1> /tmp/key
|
||||||
|
# key=`bash swarm/cmd/bzzup.sh $2 86$1`
|
||||||
|
cat /tmp/key
|
||||||
|
}
|
||||||
|
|
||||||
|
function download {
|
||||||
|
echo "download '$2' from node $1 to '$3'"
|
||||||
|
# echo attach $1 "--exec 'bzz.download(\"$2\", \"$3\")'"
|
||||||
|
attach $1 "--exec 'bzz.download(\"$2\", \"$3\")'" > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function down {
|
||||||
|
echo -n "Download hash '$2' from node $1... "
|
||||||
|
# echo "wget -O- http://localhost:86$1/$2 > /dev/null 2>&1 && echo 'got it' || echo 'not found'"
|
||||||
|
# wget -O- http://localhost:86$1/$2 > /dev/null 2>&1 && echo "got it" || echo "not found"
|
||||||
|
while true; do
|
||||||
|
attach $1 "--exec 'bzz.get(\"$2\")'" 2> /dev/null |grep -qil "status" && break
|
||||||
|
sleep 1
|
||||||
|
echo -n "."
|
||||||
|
if ((i++>10)); then
|
||||||
|
echo "not found"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "found OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
function clean { #index
|
||||||
|
echo "Clean up for $1"
|
||||||
|
rm -rf $root/$network_id/data/$1/{bzz/*/chunks,bzz/*/requests/,bzz/*/bzz-peers.json,chaindata,nodes}
|
||||||
|
}
|
||||||
|
|
||||||
|
function info {
|
||||||
|
echo "swarm node information"
|
||||||
|
echo "ROOTDIR: $root"
|
||||||
|
echo "DATADIR: $root/$network_id/data/$1"
|
||||||
|
echo "LOGFILE: $root/$network_id/log/$1.log"
|
||||||
|
echo "HTTPAPI: http://localhost:322$1"
|
||||||
|
echo "ETHPORT: 303$1"
|
||||||
|
echo "RPCPORT: 302$1"
|
||||||
|
echo "ACCOUNT:" 0x`ls -1 $root/$network_id/data/$1/bzz`
|
||||||
|
echo "CHEQUEB:" `cat $root/$network_id/data/$1/bzz/*/config.json|grep Contract|awk -F\" '{print $4}'`
|
||||||
|
echo "ROOTDIR: $root"
|
||||||
|
echo "DATADIR: $root/$network_id/data/$1"
|
||||||
|
echo "LOGFILE: $root/$network_id/log/$1.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function status {
|
||||||
|
attach 00 -exec "'console.log(eth.getBalance(eth.accounts[0])); console.log(eth.getBalance(bzz.info().Swap.Contract)); console.log(chequebook.balance)'"
|
||||||
|
}
|
||||||
|
|
||||||
|
function netstatconf {
|
||||||
|
begin=$1
|
||||||
|
N=$2
|
||||||
|
name_prefix=$3
|
||||||
|
ws_server=$4
|
||||||
|
ws_secret=$5
|
||||||
|
conf="$root/$network_id/$name_prefix.netstat.json"
|
||||||
|
|
||||||
|
echo "writing netstat conf for cluster $name_prefix to $conf"
|
||||||
|
|
||||||
|
echo -e "[" > $conf
|
||||||
|
|
||||||
|
for ((i=$begin;i<$start+$N;++i)); do
|
||||||
|
id=`printf "%02d" $i`
|
||||||
|
single_template=" {\n \"name\" : \"$name_prefix-$i\",\n \"cwd\" : \".\",\n \"script\" : \"app.js\",\n \"log_date_format\" : \"YYYY-MM-DD HH:mm Z\",\n \"merge_logs\" : false,\n \"watch\" : false,\n \"exec_interpreter\" : \"node\",\n \"exec_mode\" : \"fork_mode\",\n \"env\":\n {\n \"NODE_ENV\" : \"production\",\n \"RPC_HOST\" : \"localhost\",\n \"RPC_PORT\" : \"302$id\",\n \"INSTANCE_NAME\" : \"$name_prefix-$i\",\n \"WS_SERVER\" : \"$ws_server\",\n \"WS_SECRET\" : \"$ws_secret\",\n }\n }"
|
||||||
|
|
||||||
|
endline=""
|
||||||
|
if (($i<$N-1)); then
|
||||||
|
# if [ "$i" -ne "$N" ]; then
|
||||||
|
endline=","
|
||||||
|
fi
|
||||||
|
echo -e "$single_template$endline" >> $conf
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "]" >> $conf
|
||||||
|
}
|
||||||
|
|
||||||
|
case $cmd in
|
||||||
|
"info" )
|
||||||
|
info $*;;
|
||||||
|
"status" )
|
||||||
|
status $*;;
|
||||||
|
"clean" )
|
||||||
|
clean $*;;
|
||||||
|
"needs" )
|
||||||
|
needs $*;;
|
||||||
|
"up" )
|
||||||
|
up $*;;
|
||||||
|
"down" )
|
||||||
|
down $*;;
|
||||||
|
"init" )
|
||||||
|
init $*;;
|
||||||
|
"start" )
|
||||||
|
start $*;;
|
||||||
|
"stop" )
|
||||||
|
stop $* ;;
|
||||||
|
"restart" )
|
||||||
|
restart $*;;
|
||||||
|
"reset" )
|
||||||
|
reset $*;;
|
||||||
|
"cluster" )
|
||||||
|
cluster $*;;
|
||||||
|
"attach" )
|
||||||
|
attach $*;;
|
||||||
|
"log" )
|
||||||
|
log $*;;
|
||||||
|
"less" )
|
||||||
|
less $*;;
|
||||||
|
"netstatconf" )
|
||||||
|
netstatconf $*;;
|
||||||
|
|
||||||
|
esac
|
||||||
28
swarm/cmd/swarm/test.sh
Normal file
28
swarm/cmd/swarm/test.sh
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
TEST_DIR=`dirname $0`
|
||||||
|
TEST_NAME=`basename $0 .sh`
|
||||||
|
TEST_TYPE=`basename $TEST_DIR`
|
||||||
|
|
||||||
|
|
||||||
|
export SWARM_BIN=$TEST_DIR/../../cmd/swarm
|
||||||
|
export GETH=$SWARM_BIN/../../../geth
|
||||||
|
export NETWORKID=322$TEST_NAME
|
||||||
|
export TMPDIR=~/BZZ/test/$TEST_TYPE
|
||||||
|
export DATA_ROOT=$TMPDIR/$NETWORKID
|
||||||
|
# alias swarm='bash $SWARM_BIN/swarm.sh $DATA_ROOT $NETWORKID'
|
||||||
|
EXTRA_ARGS=$*
|
||||||
|
|
||||||
|
rm -rf $DATA_ROOT
|
||||||
|
|
||||||
|
wait=1
|
||||||
|
|
||||||
|
function swarm {
|
||||||
|
# echo bash $SWARM_BIN/swarm.sh $TMPDIR $NETWORKID $* $EXTRA_ARGS
|
||||||
|
bash $SWARM_BIN/swarm.sh $TMPDIR $NETWORKID $* $EXTRA_ARGS
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function randomfile {
|
||||||
|
dd if=/dev/urandom of=/dev/stdout bs=1024 count=$1 2>/dev/null
|
||||||
|
}
|
||||||
135
swarm/examples/album/file-manager.js
Normal file
135
swarm/examples/album/file-manager.js
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
function uploadFile(files, nr, uri) {
|
||||||
|
// when uploading complete - redirect to new address
|
||||||
|
if (files.length <= nr) {
|
||||||
|
if (uri != "") {
|
||||||
|
onUploadingComplete(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentFile = files[nr];
|
||||||
|
if (isNotImage(currentFile.type)) {
|
||||||
|
uploadFile(files, nr + 1, uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.onreadystatechange = function () {
|
||||||
|
if (xhr.readyState === 4) {
|
||||||
|
var newHash = xhr.responseText;
|
||||||
|
console.log("New hash - " + newHash);
|
||||||
|
if (newHash.length != 64) {
|
||||||
|
// something wrong
|
||||||
|
console.log("Something wrong on uploading");
|
||||||
|
alert('Oh, error on PUT file to BZZ. See log for more information.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertImage(files, nr, newHash, currentFile.name, function () {
|
||||||
|
uploadFile(files, nr + 1, "/bzz:/" + newHash + "/");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.open("PUT", uri + "imgs/" + currentFile.name, true);
|
||||||
|
xhr.setRequestHeader('Content-Type', currentFile.type);
|
||||||
|
|
||||||
|
readFile(currentFile, function (result) {
|
||||||
|
xhr.send(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFile(file, onComplete) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function (evt) {
|
||||||
|
if (onComplete) {
|
||||||
|
onComplete(evt.target.result)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertImage(files, nr, newHash, fileName, onComplete) {
|
||||||
|
// insert image into index
|
||||||
|
var img = new Image();
|
||||||
|
img.onload = function () {
|
||||||
|
var blur = imageToUrl(img, 5, 5);
|
||||||
|
var thumbData = [];
|
||||||
|
var thumbSize = 200;
|
||||||
|
if (img.naturalWidth > img.naturalHeight) {
|
||||||
|
// landscape thumbnail
|
||||||
|
var h = img.naturalHeight * thumbSize / img.naturalWidth;
|
||||||
|
thumbData[0] = imageToUrl(img, thumbSize, h);
|
||||||
|
thumbData[1] = [thumbSize, h];
|
||||||
|
} else if (img.naturalWidth < img.naturalHeight) {
|
||||||
|
// portrait thumbnail
|
||||||
|
var w = img.naturalWidth * thumbSize / img.naturalHeight;
|
||||||
|
thumbData[0] = imageToUrl(img, w, thumbSize);
|
||||||
|
thumbData[1] = [w, thumbSize];
|
||||||
|
} else {
|
||||||
|
// square
|
||||||
|
thumbData[0] = imageToUrl(img, thumbSize, thumbSize);
|
||||||
|
thumbData[1] = [thumbSize, thumbSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery('#currentPreview').attr('src', thumbData[0]);
|
||||||
|
// update index
|
||||||
|
var imgData = [];
|
||||||
|
imgData[0] = "imgs/" + fileName;
|
||||||
|
imgData[1] = [img.naturalWidth, img.naturalHeight];
|
||||||
|
imgs.data.splice(eidx, 0, {img: imgData, thumb: thumbData, blur: blur});
|
||||||
|
if (onComplete) {
|
||||||
|
onComplete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
img.onerror = function () {
|
||||||
|
if (onComplete) {
|
||||||
|
onComplete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src = "/bzz:/" + newHash + "/imgs/" + fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNotImage(type) {
|
||||||
|
var imageType = /^image\//;
|
||||||
|
return !imageType.test(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUploadingComplete(uri) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.onreadystatechange = function () {
|
||||||
|
if (xhr.readyState === 4) {
|
||||||
|
var i = xhr.responseText;
|
||||||
|
window.location.replace("/bzz:/" + i + "/");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sendImages(xhr, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFiles(files) {
|
||||||
|
uploadFile(files, 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendImages(xhr, uri) {
|
||||||
|
// set up request
|
||||||
|
xhr.open("PUT", uri + "data.json", true);
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
|
// send the collected data as JSON
|
||||||
|
xhr.send(JSON.stringify(imgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// do it because I love jQuery
|
||||||
|
function jqueryInit() {
|
||||||
|
// setup upload file selector
|
||||||
|
var fileElem = jQuery("#fileElem");
|
||||||
|
jQuery("#fileSelect").on("click", function (e) {
|
||||||
|
if (fileElem) {
|
||||||
|
fileElem.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
}
|
||||||
BIN
swarm/examples/album/images/favicon.ico
Normal file
BIN
swarm/examples/album/images/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 305 B |
|
|
@ -289,3 +289,7 @@ img
|
||||||
{
|
{
|
||||||
background: url(cut-mov.png) top left repeat-y, url(cut-mov.png) top right repeat-y;
|
background: url(cut-mov.png) top left repeat-y, url(cut-mov.png) top right repeat-y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#currentPreview {
|
||||||
|
max-height: 120px;
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,26 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
|
<link rel="shortcut icon" href="images/favicon.ico"/>
|
||||||
|
<script src="jquery.js"></script>
|
||||||
|
<script>
|
||||||
|
$.noConflict();
|
||||||
|
</script>
|
||||||
<script src="mootools-core-1.4.js" type="text/javascript"></script>
|
<script src="mootools-core-1.4.js" type="text/javascript"></script>
|
||||||
<script src="mootools-more-1.4.js" type="text/javascript"></script>
|
<script src="mootools-more-1.4.js" type="text/javascript"></script>
|
||||||
<script src="mootools-idle.js" type="text/javascript"></script>
|
<script src="mootools-idle.js" type="text/javascript"></script>
|
||||||
<script src="mootools-mooswipe.js" type="text/javascript"></script>
|
<script src="mootools-mooswipe.js" type="text/javascript"></script>
|
||||||
|
<script src="file-manager.js" type="text/javascript"></script>
|
||||||
<script src="index.js" type="text/javascript"></script>
|
<script src="index.js" type="text/javascript"></script>
|
||||||
<link href="index.css" rel="stylesheet" type="text/css"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<h2>Frak! JavaScript required :'(</h2>
|
|
||||||
</noscript>
|
|
||||||
<div id="gallery"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
|
<link href="index.css" rel="stylesheet" type="text/css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<h2>Frak! JavaScript required :'(</h2>
|
||||||
|
</noscript>
|
||||||
|
<div id="gallery"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
// Copyright(c) 2003-2014 by wave++ "Yuri D'Elia" <wavexx@thregr.org>
|
// Copyright(c) 2003-2014 by wave++ "Yuri D'Elia" <wavexx@thregr.org>
|
||||||
// Distributed under GPL2 (see COPYING) WITHOUT ANY WARRANTY.
|
// Distributed under GPL2 (see COPYING) WITHOUT ANY WARRANTY.
|
||||||
var datafile = 'data.json';
|
var datafile = 'data.json';
|
||||||
var padding = 22;
|
var padding = 100;
|
||||||
|
var marginTop = 50;
|
||||||
var duration = 500;
|
var duration = 500;
|
||||||
var thrdelay = 1500;
|
var thrdelay = 1500;
|
||||||
var hidedelay = 3000;
|
var hidedelay = 3000;
|
||||||
|
|
@ -41,6 +42,7 @@ var eback; // background
|
||||||
var enoise; // additive noise
|
var enoise; // additive noise
|
||||||
var eflash; // flashing object
|
var eflash; // flashing object
|
||||||
var ehdr; // header
|
var ehdr; // header
|
||||||
|
var progress; // progress
|
||||||
var elist; // thumbnail list
|
var elist; // thumbnail list
|
||||||
var fscr; // thumbnail list scroll fx
|
var fscr; // thumbnail list scroll fx
|
||||||
var econt; // picture container
|
var econt; // picture container
|
||||||
|
|
@ -231,7 +233,7 @@ function resizeMainImg(img)
|
||||||
img.setStyles(
|
img.setStyles(
|
||||||
{
|
{
|
||||||
'position': 'absolute',
|
'position': 'absolute',
|
||||||
'top': contSize.y / 2 - img.height / 2,
|
'top': (contSize.y / 2 - img.height / 2) + marginTop,
|
||||||
'left': contSize.x / 2 - img.width / 2
|
'left': contSize.x / 2 - img.width / 2
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -264,15 +266,6 @@ function centerThumb(duration)
|
||||||
fscr = new Fx.Scroll(elist, { duration: duration }).start(x, y);
|
fscr = new Fx.Scroll(elist, { duration: duration }).start(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendImgs(xhr, uri) {
|
|
||||||
// set up request
|
|
||||||
xhr.open("PUT", uri + "data.json", true);
|
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
|
||||||
|
|
||||||
// send the collected data as JSON
|
|
||||||
xhr.send(JSON.stringify(imgs));
|
|
||||||
}
|
|
||||||
|
|
||||||
function imageToUrl(img, w, h) {
|
function imageToUrl(img, w, h) {
|
||||||
var can = document.createElement('canvas');
|
var can = document.createElement('canvas');
|
||||||
can.width = w;
|
can.width = w;
|
||||||
|
|
@ -282,70 +275,6 @@ function imageToUrl(img, w, h) {
|
||||||
return can.toDataURL();
|
return can.toDataURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadFile(files, nr, uri) {
|
|
||||||
if(files.length <= nr) {
|
|
||||||
if(uri != "") {
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.onreadystatechange = function() { if (xhr.readyState === 4) {
|
|
||||||
var i = xhr.responseText;
|
|
||||||
window.location.replace("/" + i + "/");
|
|
||||||
}};
|
|
||||||
sendImgs(xhr, uri);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var imageType = /^image\//;
|
|
||||||
var file = files[nr];
|
|
||||||
if(!imageType.test(file.type)) {
|
|
||||||
uploadFile(files, nr + 1, uri);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.onreadystatechange = function() { if (xhr.readyState === 4) {
|
|
||||||
var i = xhr.responseText;
|
|
||||||
|
|
||||||
// insert image into index
|
|
||||||
var img = new Image();
|
|
||||||
img.onload = function() {
|
|
||||||
var blur = imageToUrl(img, 5, 5);
|
|
||||||
var thumbData = [];
|
|
||||||
var thumbSize = 200;
|
|
||||||
if(img.naturalWidth > img.naturalHeight) {
|
|
||||||
// landscape thumbnail
|
|
||||||
var h = img.naturalHeight * thumbSize / img.naturalWidth;
|
|
||||||
thumbData[0] = imageToUrl(img, thumbSize, h);
|
|
||||||
thumbData[1] = [thumbSize, h];
|
|
||||||
} else {
|
|
||||||
// portrait thumbnail
|
|
||||||
var w = img.naturalWidth * thumbSize / img.naturalHeight;
|
|
||||||
thumbData[0] = imageToUrl(img, w, thumbsize);
|
|
||||||
thumbData[1] = [w, thumbSize];
|
|
||||||
}
|
|
||||||
// update index
|
|
||||||
var imgData = [];
|
|
||||||
imgData[0] = "imgs/" + file.name;
|
|
||||||
imgData[1] = [img.naturalWidth, img.naturalHeight];
|
|
||||||
imgs.data.splice(eidx, 0, {img: imgData, thumb: thumbData, blur: blur});
|
|
||||||
uploadFile(files, nr + 1, "/" + i + "/");
|
|
||||||
}
|
|
||||||
img.src = "/" + i + "/imgs/" + file.name;
|
|
||||||
return;
|
|
||||||
}};
|
|
||||||
xhr.open("PUT", uri + "imgs/" + file.name, true);
|
|
||||||
xhr.setRequestHeader('Content-Type', file.type);
|
|
||||||
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.onload = function(evt) {
|
|
||||||
xhr.send(evt.target.result);
|
|
||||||
};
|
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFiles(files) {
|
|
||||||
uploadFile(files, 0, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteImg()
|
function deleteImg()
|
||||||
{
|
{
|
||||||
if(imgs.data.length < 2) return; // empty albums not allowed
|
if(imgs.data.length < 2) return; // empty albums not allowed
|
||||||
|
|
@ -361,13 +290,13 @@ function deleteImg()
|
||||||
var xhrd = new XMLHttpRequest();
|
var xhrd = new XMLHttpRequest();
|
||||||
xhrd.onreadystatechange = function () { if (xhrd.readyState === 4) {
|
xhrd.onreadystatechange = function () { if (xhrd.readyState === 4) {
|
||||||
var j = xhrd.responseText;
|
var j = xhrd.responseText;
|
||||||
window.location.replace("/" + j + "/");
|
window.location.replace("/bzz:/" + j + "/");
|
||||||
}};
|
}};
|
||||||
xhrd.open("DELETE", "/" + i + "/" + fname, true);
|
xhrd.open("DELETE", "/bzz%3A/" + i + "/" + fname, true);
|
||||||
xhrd.send();
|
xhrd.send();
|
||||||
}};
|
}};
|
||||||
|
|
||||||
sendImgs(xhr, "");
|
sendImages(xhr, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveUpDown(off)
|
function moveUpDown(off)
|
||||||
|
|
@ -378,9 +307,9 @@ function moveUpDown(off)
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.onreadystatechange = function () { if (xhr.readyState === 4) {
|
xhr.onreadystatechange = function () { if (xhr.readyState === 4) {
|
||||||
var i = xhr.responseText;
|
var i = xhr.responseText;
|
||||||
window.location.replace("/" + i + "/#" + (eidx + off));
|
window.location.replace("/bzz:/" + i + "/#" + (eidx + off));
|
||||||
}};
|
}};
|
||||||
sendImgs(xhr, "");
|
sendImages(xhr, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveUp()
|
function moveUp()
|
||||||
|
|
@ -435,15 +364,8 @@ function onMainReady()
|
||||||
ehdr.set('html', dsc.join(' '));
|
ehdr.set('html', dsc.join(' '));
|
||||||
ehdr.setStyle('display', (dsc.length? 'block': 'none'));
|
ehdr.setStyle('display', (dsc.length? 'block': 'none'));
|
||||||
|
|
||||||
// setup upload file selector
|
progress.set('html', '<img id="currentPreview">');
|
||||||
var fileSelect = document.getElementById("fileSelect"),
|
progress.set('style', 'text-align: center; padding-top: 20px');
|
||||||
fileElem = document.getElementById("fileElem");
|
|
||||||
fileSelect.addEventListener("click", function (e) {
|
|
||||||
if (fileElem) {
|
|
||||||
fileElem.click();
|
|
||||||
}
|
|
||||||
e.preventDefault(); // prevent navigation to "#"
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
// complete thumbnails
|
// complete thumbnails
|
||||||
var d = duration;
|
var d = duration;
|
||||||
|
|
@ -494,6 +416,8 @@ function onMainReady()
|
||||||
var data = imgs.data[eidx + 1];
|
var data = imgs.data[eidx + 1];
|
||||||
Asset.images([data.img[0], data.blur]);
|
Asset.images([data.img[0], data.blur]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jqueryInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showThrobber()
|
function showThrobber()
|
||||||
|
|
@ -677,6 +601,9 @@ function initGallery(data)
|
||||||
ehdr = new Element('div', { id: 'header' });
|
ehdr = new Element('div', { id: 'header' });
|
||||||
ehdr.inject(econt);
|
ehdr.inject(econt);
|
||||||
|
|
||||||
|
progress = new Element('div', { id: 'progress' });
|
||||||
|
progress.inject(econt);
|
||||||
|
|
||||||
elist = new Element('div', { id: 'list' });
|
elist = new Element('div', { id: 'list' });
|
||||||
elist.inject(emain);
|
elist.inject(emain);
|
||||||
|
|
||||||
|
|
|
||||||
4
swarm/examples/album/jquery.js
vendored
Normal file
4
swarm/examples/album/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -6,10 +6,21 @@
|
||||||
navigator.registerProtocolHandler("bzz",
|
navigator.registerProtocolHandler("bzz",
|
||||||
"http://localhost:8500/%s",
|
"http://localhost:8500/%s",
|
||||||
"Swarm handler");
|
"Swarm handler");
|
||||||
|
navigator.registerProtocolHandler("bzzi",
|
||||||
|
"http://localhost:8500/%s",
|
||||||
|
"Swarm immutable handler");
|
||||||
|
navigator.registerProtocolHandler("bzzr",
|
||||||
|
"http://localhost:8500/%s",
|
||||||
|
"Swarm raw handler");
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Register Swarm protocol handler</h1>
|
<h1>Register Swarm protocol handler</h1>
|
||||||
<p>This web page will install a web protocol handler for the <code>bzz:</code> protocol.</p>
|
<p>This web page will install a web protocol handler for
|
||||||
|
<code>bzz:</code>,
|
||||||
|
<code>bzzi:</code> and
|
||||||
|
<code>bzzr:</code>
|
||||||
|
protocols.
|
||||||
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler for storage/retrieval related protocol requests
|
// Handler for storage/retrieval related protocol requests
|
||||||
|
|
@ -51,6 +51,7 @@ func (self *Depo) HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// set peers state to persist
|
||||||
p.syncState = req.State
|
p.syncState = req.State
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +125,11 @@ func (self *Depo) HandleRetrieveRequestMsg(req *retrieveRequestMsgData, p *peer)
|
||||||
req.from = p
|
req.from = p
|
||||||
// swap - record credit for 1 request
|
// swap - record credit for 1 request
|
||||||
// note that only charge actual reqsearches
|
// note that only charge actual reqsearches
|
||||||
if err := p.swap.Add(1); err != nil {
|
var err error
|
||||||
|
if p.swap != nil {
|
||||||
|
err = p.swap.Add(1)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infof("[BZZ] Depo.HandleRetrieveRequest: %v - cannot process request: %v", req.Key.Log(), err)
|
glog.V(logger.Warn).Infof("[BZZ] Depo.HandleRetrieveRequest: %v - cannot process request: %v", req.Key.Log(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
const requesterCount = 3
|
const requesterCount = 3
|
||||||
|
|
@ -18,7 +18,6 @@ via the native bzz protocol
|
||||||
which uses an MSB logarithmic distance-based semi-permanent Kademlia table for
|
which uses an MSB logarithmic distance-based semi-permanent Kademlia table for
|
||||||
* recursive forwarding style routing for retrieval
|
* recursive forwarding style routing for retrieval
|
||||||
* smart syncronisation
|
* smart syncronisation
|
||||||
* TODO: beeline delivery, IPFS, IPΞS
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type forwarder struct {
|
type forwarder struct {
|
||||||
|
|
@ -42,26 +41,30 @@ var searchTimeout = 3 * time.Second
|
||||||
func (self *forwarder) Retrieve(chunk *storage.Chunk) {
|
func (self *forwarder) Retrieve(chunk *storage.Chunk) {
|
||||||
peers := self.hive.getPeers(chunk.Key, 0)
|
peers := self.hive.getPeers(chunk.Key, 0)
|
||||||
glog.V(logger.Detail).Infof("[BZZ] forwarder.Retrieve: %v - received %d peers from KΛÐΞMLIΛ...", chunk.Key.Log(), len(peers))
|
glog.V(logger.Detail).Infof("[BZZ] forwarder.Retrieve: %v - received %d peers from KΛÐΞMLIΛ...", chunk.Key.Log(), len(peers))
|
||||||
|
OUT:
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
glog.V(logger.Detail).Infof("[BZZ] forwarder.Retrieve: sending retrieveRequest %v to peer [%v]", chunk.Key.Log(), p)
|
glog.V(logger.Detail).Infof("[BZZ] forwarder.Retrieve: sending retrieveRequest %v to peer [%v]", chunk.Key.Log(), p)
|
||||||
var req *retrieveRequestMsgData
|
|
||||||
OUT:
|
|
||||||
for _, recipients := range chunk.Req.Requesters {
|
for _, recipients := range chunk.Req.Requesters {
|
||||||
for _, recipient := range recipients {
|
for _, recipient := range recipients {
|
||||||
req := recipient.(*retrieveRequestMsgData)
|
req := recipient.(*retrieveRequestMsgData)
|
||||||
if req.from.Addr() == p.Addr() {
|
if req.from.Addr() == p.Addr() {
|
||||||
break OUT
|
continue OUT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req != nil {
|
req := &retrieveRequestMsgData{
|
||||||
if err := p.swap.Add(-1); err == nil {
|
Key: chunk.Key,
|
||||||
p.retrieve(req)
|
Id: generateId(),
|
||||||
break
|
|
||||||
} else {
|
|
||||||
glog.V(logger.Warn).Infof("[BZZ] forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var err error
|
||||||
|
if p.swap != nil {
|
||||||
|
err = p.swap.Add(-1)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
p.retrieve(req)
|
||||||
|
break OUT
|
||||||
|
}
|
||||||
|
glog.V(logger.Warn).Infof("[BZZ] forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,14 +82,14 @@ func (self *forwarder) Store(chunk *storage.Chunk) {
|
||||||
source = chunk.Source.(*peer)
|
source = chunk.Source.(*peer)
|
||||||
}
|
}
|
||||||
for _, p := range self.hive.getPeers(chunk.Key, 0) {
|
for _, p := range self.hive.getPeers(chunk.Key, 0) {
|
||||||
glog.V(logger.Detail).Infof("[BZZ] %v %v", p, chunk)
|
glog.V(logger.Detail).Infof("[BZZ] forwarder.Store: %v %v", p, chunk)
|
||||||
|
|
||||||
if source == nil || p.Addr() != source.Addr() {
|
if p.syncer != nil && (source == nil || p.Addr() != source.Addr()) {
|
||||||
n++
|
n++
|
||||||
Deliver(p, msg, PropagateReq)
|
Deliver(p, msg, PropagateReq)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("[BZZ] forwarder.Store: sent to %v ps (chunk = %v)", n, chunk)
|
glog.V(logger.Detail).Infof("[BZZ] forwarder.Store: sent to %v peers (chunk = %v)", n, chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// once a chunk is found deliver it to its requesters unless timed out
|
// once a chunk is found deliver it to its requesters unless timed out
|
||||||
|
|
@ -104,7 +107,7 @@ func (self *forwarder) Deliver(chunk *storage.Chunk) {
|
||||||
for id, r := range requesters {
|
for id, r := range requesters {
|
||||||
req = r.(*retrieveRequestMsgData)
|
req = r.(*retrieveRequestMsgData)
|
||||||
if req.timeout == nil || req.timeout.After(time.Now()) {
|
if req.timeout == nil || req.timeout.After(time.Now()) {
|
||||||
glog.V(logger.Ridiculousness).Infof("[BZZ] forwarder.Deliver: %v -> %v", req.Id, req.from)
|
glog.V(logger.Detail).Infof("[BZZ] forwarder.Deliver: %v -> %v", req.Id, req.from)
|
||||||
msg.Id = uint64(id)
|
msg.Id = uint64(id)
|
||||||
Deliver(req.from, msg, DeliverReq)
|
Deliver(req.from, msg, DeliverReq)
|
||||||
n++
|
n++
|
||||||
|
|
@ -114,7 +117,7 @@ func (self *forwarder) Deliver(chunk *storage.Chunk) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("[BZZ] NetStore.Deliver: submit chunk %v (request id %v) for delivery to %v peers", chunk.Key.Log(), id, n)
|
glog.V(logger.Detail).Infof("[BZZ] forwarder.Deliver: submit chunk %v (request id %v) for delivery to %v peers", chunk.Key.Log(), id, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/kademlia"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/network/kademlia"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -32,13 +32,19 @@ type Hive struct {
|
||||||
path string
|
path string
|
||||||
toggle chan bool
|
toggle chan bool
|
||||||
more chan bool
|
more chan bool
|
||||||
|
|
||||||
|
// for testing only
|
||||||
|
swapEnabled bool
|
||||||
|
syncEnabled bool
|
||||||
|
blockRead bool
|
||||||
|
blockWrite bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
callInterval = 10000000000
|
callInterval = 3000000000
|
||||||
bucketSize = 3
|
// bucketSize = 3
|
||||||
maxProx = 10
|
// maxProx = 8
|
||||||
proxBinSize = 8
|
// proxBinSize = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
type HiveParams struct {
|
type HiveParams struct {
|
||||||
|
|
@ -49,9 +55,9 @@ type HiveParams struct {
|
||||||
|
|
||||||
func NewHiveParams(path string) *HiveParams {
|
func NewHiveParams(path string) *HiveParams {
|
||||||
kad := kademlia.NewKadParams()
|
kad := kademlia.NewKadParams()
|
||||||
kad.BucketSize = bucketSize
|
// kad.BucketSize = bucketSize
|
||||||
kad.MaxProx = maxProx
|
// kad.MaxProx = maxProx
|
||||||
kad.ProxBinSize = proxBinSize
|
// kad.ProxBinSize = proxBinSize
|
||||||
|
|
||||||
return &HiveParams{
|
return &HiveParams{
|
||||||
CallInterval: callInterval,
|
CallInterval: callInterval,
|
||||||
|
|
@ -60,16 +66,34 @@ func NewHiveParams(path string) *HiveParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHive(addr common.Hash, params *HiveParams) *Hive {
|
func NewHive(addr common.Hash, params *HiveParams, swapEnabled, syncEnabled bool) *Hive {
|
||||||
kad := kademlia.New(kademlia.Address(addr), params.KadParams)
|
kad := kademlia.New(kademlia.Address(addr), params.KadParams)
|
||||||
return &Hive{
|
return &Hive{
|
||||||
callInterval: params.CallInterval,
|
callInterval: params.CallInterval,
|
||||||
kad: kad,
|
kad: kad,
|
||||||
addr: kad.Addr(),
|
addr: kad.Addr(),
|
||||||
path: params.KadDbPath,
|
path: params.KadDbPath,
|
||||||
|
swapEnabled: swapEnabled,
|
||||||
|
syncEnabled: syncEnabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Hive) SyncEnabled(on bool) {
|
||||||
|
self.syncEnabled = on
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Hive) SwapEnabled(on bool) {
|
||||||
|
self.swapEnabled = on
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Hive) BlockNetworkRead(on bool) {
|
||||||
|
self.blockRead = on
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Hive) BlockNetworkWrite(on bool) {
|
||||||
|
self.blockWrite = on
|
||||||
|
}
|
||||||
|
|
||||||
// public accessor to the hive base address
|
// public accessor to the hive base address
|
||||||
func (self *Hive) Addr() kademlia.Address {
|
func (self *Hive) Addr() kademlia.Address {
|
||||||
return self.addr
|
return self.addr
|
||||||
|
|
@ -122,7 +146,7 @@ func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPee
|
||||||
} else {
|
} else {
|
||||||
self.toggle <- false
|
self.toggle <- false
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: queen's address: %v, population: %d (%d)\n%v", self.addr, self.kad.Count(), self.kad.DBCount(), self.kad)
|
glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: queen's address: %v, population: %d (%d)", self.addr, self.kad.Count(), self.kad.DBCount())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return
|
return
|
||||||
|
|
@ -134,7 +158,7 @@ func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPee
|
||||||
// wake state is toggled by writing to self.toggle
|
// wake state is toggled by writing to self.toggle
|
||||||
// it restarts if the table becomes non-full again due to disconnections
|
// it restarts if the table becomes non-full again due to disconnections
|
||||||
func (self *Hive) keepAlive() {
|
func (self *Hive) keepAlive() {
|
||||||
var alarm <-chan time.Time
|
alarm := time.NewTicker(time.Duration(self.callInterval)).C
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-alarm:
|
case <-alarm:
|
||||||
|
|
@ -185,14 +209,14 @@ func (self *Hive) addPeer(p *peer) {
|
||||||
|
|
||||||
// called after peer disconnected
|
// called after peer disconnected
|
||||||
func (self *Hive) removePeer(p *peer) {
|
func (self *Hive) removePeer(p *peer) {
|
||||||
glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: bee %v gone offline", p)
|
glog.V(logger.Debug).Infof("[BZZ] KΛÐΞMLIΛ hive: bee %v removed", p)
|
||||||
self.kad.Off(p, saveSync)
|
self.kad.Off(p, saveSync)
|
||||||
select {
|
select {
|
||||||
case self.more <- true:
|
case self.more <- true:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
if self.kad.Count() == 0 {
|
if self.kad.Count() == 0 {
|
||||||
glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: empty, all bees gone", p)
|
glog.V(logger.Debug).Infof("[BZZ] KΛÐΞMLIΛ hive: empty, all bees gone")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,7 +232,7 @@ func (self *Hive) getPeers(target storage.Key, max int) (peers []*peer) {
|
||||||
|
|
||||||
// disconnects all the peers
|
// disconnects all the peers
|
||||||
func (self *Hive) DropAll() {
|
func (self *Hive) DropAll() {
|
||||||
glog.V(logger.Detail).Infof("[BZZ] KΛÐΞMLIΛ hive: dropping all bees")
|
glog.V(logger.Info).Infof("[BZZ] KΛÐΞMLIΛ hive: dropping all bees")
|
||||||
for _, node := range self.kad.FindClosest(kademlia.Address{}, 0) {
|
for _, node := range self.kad.FindClosest(kademlia.Address{}, 0) {
|
||||||
node.Drop()
|
node.Drop()
|
||||||
}
|
}
|
||||||
|
|
@ -260,17 +284,22 @@ func (self *peer) LastActive() time.Time {
|
||||||
// reads the serialised form of sync state persisted as the 'Meta' attribute
|
// reads the serialised form of sync state persisted as the 'Meta' attribute
|
||||||
// and sets the decoded syncState on the online node
|
// and sets the decoded syncState on the online node
|
||||||
func loadSync(record *kademlia.NodeRecord, node kademlia.Node) error {
|
func loadSync(record *kademlia.NodeRecord, node kademlia.Node) error {
|
||||||
if p, ok := node.(*peer); ok {
|
p, ok := node.(*peer)
|
||||||
if record.Meta == nil {
|
if !ok {
|
||||||
glog.V(logger.Debug).Infof("no sync state for node record %v", record)
|
return fmt.Errorf("invalid type")
|
||||||
return nil
|
|
||||||
}
|
|
||||||
state, err := decodeSync(record.Meta)
|
|
||||||
glog.V(logger.Debug).Infof("sync state for node record %v: %s -> %v", record, string(*(record.Meta)), state)
|
|
||||||
p.syncState = state
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf("invalid type")
|
if record.Meta == nil {
|
||||||
|
glog.V(logger.Debug).Infof("no sync state for node record %v setting default", record)
|
||||||
|
p.syncState = &syncState{DbSyncState: &storage.DbSyncState{}}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
state, err := decodeSync(record.Meta)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error decoding kddb record meta info into a sync state: %v", err)
|
||||||
|
}
|
||||||
|
glog.V(logger.Detail).Infof("sync state for node record %v read from Meta: %s", record, string(*(record.Meta)))
|
||||||
|
p.syncState = state
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback when saving a sync state
|
// callback when saving a sync state
|
||||||
|
|
@ -281,8 +310,7 @@ func saveSync(record *kademlia.NodeRecord, node kademlia.Node) {
|
||||||
glog.V(logger.Warn).Infof("error saving sync state for %v: %v", node, err)
|
glog.V(logger.Warn).Infof("error saving sync state for %v: %v", node, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
glog.V(logger.Warn).Infof("saving sync state for %v: %s", node, string(*meta))
|
glog.V(logger.Detail).Infof("saved sync state for %v: %s", node, string(*meta))
|
||||||
|
|
||||||
record.Meta = meta
|
record.Meta = meta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -320,3 +348,7 @@ func (self *Hive) peers(req *retrieveRequestMsgData) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Hive) String() string {
|
||||||
|
return self.kad.String()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,9 @@ func (self *KadDb) add(nrs []*NodeRecord, proximityBin func(Address) int) {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("[KΛÐ]: received %d node records, added %d new", len(nrs), n)
|
if n > 0 {
|
||||||
|
glog.V(logger.Debug).Infof("[KΛÐ]: %d/%d node records (new/known)", n, len(nrs))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -145,7 +147,8 @@ We check for missing online nodes in the buckets for 1 upto Max BucketSize round
|
||||||
On each round we proceed from the low to high proximity order buckets.
|
On each round we proceed from the low to high proximity order buckets.
|
||||||
If the number of active nodes (=connected peers) is < rounds, then start looking
|
If the number of active nodes (=connected peers) is < rounds, then start looking
|
||||||
for a known candidate. To determine if there is a candidate to recommend the
|
for a known candidate. To determine if there is a candidate to recommend the
|
||||||
node record database row corresponding to the bucket is checked.a
|
node record database row corresponding to the bucket is checked.
|
||||||
|
|
||||||
If the row cursor is on position i, the ith element in the row is chosen.
|
If the row cursor is on position i, the ith element in the row is chosen.
|
||||||
If the record is scheduled not to be retried before NOW, the next element is taken.
|
If the record is scheduled not to be retried before NOW, the next element is taken.
|
||||||
If the record is scheduled can be retried, it is set as checked, scheduled for
|
If the record is scheduled can be retried, it is set as checked, scheduled for
|
||||||
|
|
@ -12,14 +12,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bucketSize = 20
|
bucketSize = 3
|
||||||
maxProx = 255
|
proxBinSize = 4
|
||||||
|
maxProx = 8
|
||||||
connRetryExp = 2
|
connRetryExp = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
purgeInterval = 42 * time.Hour
|
purgeInterval = 42 * time.Hour
|
||||||
initialRetryInterval = 42 * 100 * time.Millisecond
|
initialRetryInterval = 42 * 100 * time.Millisecond
|
||||||
|
maxIdleInterval = 42 * 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
type KadParams struct {
|
type KadParams struct {
|
||||||
|
|
@ -35,7 +37,7 @@ type KadParams struct {
|
||||||
func NewKadParams() *KadParams {
|
func NewKadParams() *KadParams {
|
||||||
return &KadParams{
|
return &KadParams{
|
||||||
MaxProx: maxProx,
|
MaxProx: maxProx,
|
||||||
ProxBinSize: bucketSize,
|
ProxBinSize: proxBinSize,
|
||||||
BucketSize: bucketSize,
|
BucketSize: bucketSize,
|
||||||
PurgeInterval: purgeInterval,
|
PurgeInterval: purgeInterval,
|
||||||
InitialRetryInterval: initialRetryInterval,
|
InitialRetryInterval: initialRetryInterval,
|
||||||
|
|
@ -72,6 +74,8 @@ func New(addr Address, params *KadParams) *Kademlia {
|
||||||
}
|
}
|
||||||
glog.V(logger.Info).Infof("[KΛÐ] base address %v", addr)
|
glog.V(logger.Info).Infof("[KΛÐ] base address %v", addr)
|
||||||
|
|
||||||
|
// ! temporary hack fixme:
|
||||||
|
params.ProxBinSize = 4
|
||||||
return &Kademlia{
|
return &Kademlia{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
KadParams: params,
|
KadParams: params,
|
||||||
|
|
@ -100,41 +104,45 @@ func (self *Kademlia) DBCount() int {
|
||||||
// On is the entry point called when a new nodes is added
|
// On is the entry point called when a new nodes is added
|
||||||
// unsafe in that node is not checked to be already active node (to be called once)
|
// unsafe in that node is not checked to be already active node (to be called once)
|
||||||
func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error) {
|
func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error) {
|
||||||
|
defer self.lock.Unlock()
|
||||||
|
self.lock.Lock()
|
||||||
|
|
||||||
index := self.proximityBin(node.Addr())
|
index := self.proximityBin(node.Addr())
|
||||||
record := self.db.findOrCreate(index, node.Addr(), node.Url())
|
record := self.db.findOrCreate(index, node.Addr(), node.Url())
|
||||||
// callback on add node
|
// callback on add node
|
||||||
// setting the node on the record, set it checked (for connectivity)
|
// setting the node on the record, set it checked (for connectivity)
|
||||||
record.node = node
|
record.node = node
|
||||||
|
|
||||||
glog.V(logger.Info).Infof("[KΛÐ]: add node record %v with node %v", record, node)
|
|
||||||
if cb != nil {
|
if cb != nil {
|
||||||
err = cb(record, node)
|
err = cb(record, node)
|
||||||
glog.V(logger.Info).Infof("[KΛÐ]: cb(%v, %v) ->%v", record, node, err)
|
glog.V(logger.Detail).Infof("[KΛÐ]: cb(%v, %v) ->%v", record, node, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("node %v not added: %v", node.Addr(), err)
|
return fmt.Errorf("unable to add node %v, callback error: %v", node.Addr(), err)
|
||||||
}
|
}
|
||||||
|
glog.V(logger.Info).Infof("[KΛÐ]: add node record %v with node %v", record, node)
|
||||||
}
|
}
|
||||||
record.connected = true
|
record.connected = true
|
||||||
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
self.lock.Lock()
|
|
||||||
|
|
||||||
// insert in kademlia table of active nodes
|
// insert in kademlia table of active nodes
|
||||||
bucket := self.buckets[index]
|
bucket := self.buckets[index]
|
||||||
// if bucket is full insertion replaces the worst node
|
// if bucket is full insertion replaces the worst node
|
||||||
// TODO: give priority to peers with active traffic
|
// TODO: give priority to peers with active traffic
|
||||||
if worst, pos := bucket.insert(node); worst != nil {
|
replaced, err := bucket.insert(node)
|
||||||
glog.V(logger.Info).Infof("[KΛÐ]: replace node %v (%d) with node %v", worst, pos, node)
|
if err != nil {
|
||||||
|
glog.V(logger.Debug).Infof("[KΛÐ]: node %v not needed: %v", node, err)
|
||||||
|
return err
|
||||||
// no prox adjustment needed
|
// no prox adjustment needed
|
||||||
// do not change count
|
// do not change count
|
||||||
} else {
|
|
||||||
glog.V(logger.Info).Infof("[KΛÐ]: add node %v to table", node)
|
|
||||||
self.count++
|
|
||||||
self.adjustProxMore(index)
|
|
||||||
}
|
}
|
||||||
|
if replaced != nil {
|
||||||
|
glog.V(logger.Debug).Infof("[KΛÐ]: node %v replaced by %v ", replaced, node)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// new node added
|
||||||
|
glog.V(logger.Info).Infof("[KΛÐ]: add node %v to table", node)
|
||||||
|
self.count++
|
||||||
|
self.setProxLimit(index, false)
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// is the entrypoint called when a node is taken offline
|
// is the entrypoint called when a node is taken offline
|
||||||
|
|
@ -161,7 +169,8 @@ func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) {
|
||||||
if len(bucket.nodes) < bucket.size {
|
if len(bucket.nodes) < bucket.size {
|
||||||
err = fmt.Errorf("insufficient nodes (%v) in bucket %v", len(bucket.nodes), index)
|
err = fmt.Errorf("insufficient nodes (%v) in bucket %v", len(bucket.nodes), index)
|
||||||
}
|
}
|
||||||
self.adjustProxLess(index)
|
|
||||||
|
self.setProxLimit(index, true)
|
||||||
|
|
||||||
r := self.db.index[node.Addr()]
|
r := self.db.index[node.Addr()]
|
||||||
// callback on remove
|
// callback on remove
|
||||||
|
|
@ -174,49 +183,40 @@ func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// proxLimit is dynamically adjusted so that 1) there is no
|
// proxLimit is dynamically adjusted so that
|
||||||
// empty buckets in bin < proxLimit and 2) the sum of all items sare the maximum
|
// 1) there is no empty buckets in bin < proxLimit and
|
||||||
// possible but lower than ProxBinSize
|
// 2) the sum of all items sare the maximpossible but lower than ProxBinSize
|
||||||
// adjust Prox (proxLimit and proxSize after an insertion of add nodes into bucket r)
|
// adjust Prox (proxLimit and proxSize after an insertion/removal of nodes)
|
||||||
func (self *Kademlia) adjustProxMore(r int) {
|
// caller holds the lock
|
||||||
if r >= self.proxLimit {
|
func (self *Kademlia) setProxLimit(r int, off bool) {
|
||||||
exLimit := self.proxLimit
|
// glog.V(logger.Info).Infof("[KΛÐ]: adjust proxbin for (bin: %v, off: %v)", r, off)
|
||||||
exSize := self.proxSize
|
if r < self.proxLimit && len(self.buckets[r].nodes) > 0 {
|
||||||
self.proxSize++
|
return
|
||||||
|
|
||||||
var i int
|
|
||||||
for i = self.proxLimit; i < self.MaxProx && len(self.buckets[i].nodes) > 0 && self.proxSize-len(self.buckets[i].nodes) > self.ProxBinSize; i++ {
|
|
||||||
self.proxSize -= len(self.buckets[i].nodes)
|
|
||||||
}
|
|
||||||
self.proxLimit = i
|
|
||||||
|
|
||||||
glog.V(logger.Detail).Infof("[KΛÐ]: Max Prox Bin: Lower Limit: %v (was %v): Bin Size: %v (was %v)", self.proxLimit, exLimit, self.proxSize, exSize)
|
|
||||||
}
|
}
|
||||||
}
|
glog.V(logger.Detail).Infof("[KΛÐ]: set proxbin (size: %v, limit: %v, bin: %v, off: %v)", self.proxSize, self.proxLimit, r, off)
|
||||||
|
if off {
|
||||||
func (self *Kademlia) adjustProxLess(r int) {
|
|
||||||
exLimit := self.proxLimit
|
|
||||||
exSize := self.proxSize
|
|
||||||
if r >= self.proxLimit {
|
|
||||||
self.proxSize--
|
self.proxSize--
|
||||||
}
|
for (self.proxSize < self.ProxBinSize || r < self.proxLimit) &&
|
||||||
|
self.proxLimit > 0 {
|
||||||
if r < self.proxLimit && len(self.buckets[r].nodes) == 0 {
|
//
|
||||||
for i := self.proxLimit - 1; i > r; i-- {
|
self.proxLimit--
|
||||||
self.proxSize += len(self.buckets[i].nodes)
|
self.proxSize += len(self.buckets[self.proxLimit].nodes)
|
||||||
|
glog.V(logger.Detail).Infof("[KΛÐ]: proxbin expansion (size: %v, limit: %v, bin: %v, off: %v)", self.proxSize, self.proxLimit, r, off)
|
||||||
}
|
}
|
||||||
self.proxLimit = r
|
glog.V(logger.Detail).Infof("%v", self)
|
||||||
} else if self.proxLimit > 0 && r >= self.proxLimit-1 {
|
return
|
||||||
var i int
|
|
||||||
for i = self.proxLimit - 1; i > 0 && len(self.buckets[i].nodes)+self.proxSize <= self.ProxBinSize; i-- {
|
|
||||||
self.proxSize += len(self.buckets[i].nodes)
|
|
||||||
}
|
|
||||||
self.proxLimit = i
|
|
||||||
}
|
}
|
||||||
|
self.proxSize++
|
||||||
|
for self.proxLimit < self.MaxProx &&
|
||||||
|
len(self.buckets[self.proxLimit].nodes) > 0 &&
|
||||||
|
self.proxSize-len(self.buckets[self.proxLimit].nodes) >= self.ProxBinSize {
|
||||||
|
//
|
||||||
|
self.proxSize -= len(self.buckets[self.proxLimit].nodes)
|
||||||
|
self.proxLimit++
|
||||||
|
glog.V(logger.Detail).Infof("[KΛÐ]: proxbin contraction (size: %v, limit: %v, bin: %v, off: %v)", self.proxSize, self.proxLimit, r, off)
|
||||||
|
}
|
||||||
|
glog.V(logger.Detail).Infof("%v", self)
|
||||||
|
|
||||||
if exLimit != self.proxLimit || exSize != self.proxSize {
|
|
||||||
glog.V(logger.Detail).Infof("[KΛÐ]: Max Prox Bin: Lower Limit: %v (was %v): Bin Size: %v (was %v)", self.proxLimit, exLimit, self.proxSize, exSize)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -251,8 +251,7 @@ func (self *Kademlia) FindClosest(target Address, max int) []Node {
|
||||||
r.push(bucket[i], limit)
|
r.push(bucket[i], limit)
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
if max == 0 && start <= index && (n > 0 || start == 0) ||
|
if max == 0 && start <= index && (n > 0 || start == 0) || max > 0 && down && start <= index && (n >= limit || n == self.count || start == 0) {
|
||||||
max > 0 && down && start <= index && (n >= limit || n == self.count || start == 0) {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if down {
|
if down {
|
||||||
|
|
@ -335,17 +334,13 @@ func (h *nodesByDistance) push(node Node, max int) {
|
||||||
// insert adds a peer to a bucket either by appending to existing items if
|
// insert adds a peer to a bucket either by appending to existing items if
|
||||||
// bucket length does not exceed bucketSize, or by replacing the worst
|
// bucket length does not exceed bucketSize, or by replacing the worst
|
||||||
// Node in the bucket
|
// Node in the bucket
|
||||||
func (self *bucket) insert(node Node) (dropped Node, pos int) {
|
func (self *bucket) insert(node Node) (replaced Node, err error) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
if len(self.nodes) >= self.size { // >= allows us to add peers beyond the bucketsize limitation
|
if len(self.nodes) >= self.size { // >= allows us to add peers beyond the bucketsize limitation
|
||||||
dropped, pos = self.worstNode()
|
// dev p2p kicks out nodes idle for > 30 s, so here we never replace nodes if
|
||||||
if dropped != nil {
|
// bucket is full
|
||||||
self.nodes[pos] = node
|
return nil, fmt.Errorf("bucket full")
|
||||||
glog.V(logger.Info).Infof("[KΛÐ] dropping node %v (%d)", dropped, pos)
|
|
||||||
dropped.Drop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.nodes = append(self.nodes, node)
|
self.nodes = append(self.nodes, node)
|
||||||
return
|
return
|
||||||
|
|
@ -357,20 +352,6 @@ func (self *bucket) length(node Node) int {
|
||||||
return len(self.nodes)
|
return len(self.nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// worst expunges the single worst node in a row, where worst entry is the node
|
|
||||||
// that has been inactive for the longests time
|
|
||||||
func (self *bucket) worstNode() (node Node, pos int) {
|
|
||||||
var oldest time.Time
|
|
||||||
for p, n := range self.nodes {
|
|
||||||
if (oldest == time.Time{}) || !oldest.Before(n.LastActive()) {
|
|
||||||
oldest = n.LastActive()
|
|
||||||
node = n
|
|
||||||
pos = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Taking the proximity order relative to a fix point x classifies the points in
|
Taking the proximity order relative to a fix point x classifies the points in
|
||||||
the space (n byte long byte sequences) into bins. Items in each are at
|
the space (n byte long byte sequences) into bins. Items in each are at
|
||||||
|
|
@ -415,10 +396,12 @@ func (self *Kademlia) Load(path string, cb func(*NodeRecord, Node) error) (err e
|
||||||
}
|
}
|
||||||
|
|
||||||
// kademlia table + kaddb table displayed with ascii
|
// kademlia table + kaddb table displayed with ascii
|
||||||
|
// callerholds the lock
|
||||||
func (self *Kademlia) String() string {
|
func (self *Kademlia) String() string {
|
||||||
|
|
||||||
var rows []string
|
var rows []string
|
||||||
rows = append(rows, "=========================================================================")
|
rows = append(rows, "=========================================================================")
|
||||||
|
rows = append(rows, fmt.Sprintf("KΛÐΞMLIΛ hive: queen's address: %v, population: %d (%d)", self.addr, self.Count(), self.DBCount()))
|
||||||
rows = append(rows, fmt.Sprintf("%v : MaxProx: %d, ProxBinSize: %d, BucketSize: %d, proxLimit: %d, proxSize: %d", time.Now(), self.MaxProx, self.ProxBinSize, self.BucketSize, self.proxLimit, self.proxSize))
|
rows = append(rows, fmt.Sprintf("%v : MaxProx: %d, ProxBinSize: %d, BucketSize: %d, proxLimit: %d, proxSize: %d", time.Now(), self.MaxProx, self.ProxBinSize, self.BucketSize, self.proxLimit, self.proxSize))
|
||||||
|
|
||||||
for i, b := range self.buckets {
|
for i, b := range self.buckets {
|
||||||
|
|
@ -5,10 +5,11 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/network/kademlia"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/chequebook"
|
||||||
"github.com/ethereum/go-ethereum/swarm/services/swap"
|
"github.com/ethereum/go-ethereum/swarm/services/swap"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
"github.com/ethereum/go-ethereum/common/chequebook"
|
|
||||||
"github.com/ethereum/go-ethereum/common/kademlia"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -76,7 +77,11 @@ func (self storeRequestMsgData) String() string {
|
||||||
} else {
|
} else {
|
||||||
from = self.from.Addr().String()
|
from = self.from.Addr().String()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("from: %v, Key: %v; ID: %v, requestTimeout: %v, storageTimeout: %v, SData %x", from, self.Key, self.Id, self.requestTimeout, self.storageTimeout, self.SData[:10])
|
end := len(self.SData)
|
||||||
|
if len(self.SData) > 10 {
|
||||||
|
end = 10
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("from: %v, Key: %v; ID: %v, requestTimeout: %v, storageTimeout: %v, SData %x", from, self.Key, self.Id, self.requestTimeout, self.storageTimeout, self.SData[:end])
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -108,7 +113,7 @@ type retrieveRequestMsgData struct {
|
||||||
MaxSize uint64 // maximum size of delivery accepted
|
MaxSize uint64 // maximum size of delivery accepted
|
||||||
MaxPeers uint64 // maximum number of peers returned
|
MaxPeers uint64 // maximum number of peers returned
|
||||||
Timeout uint64 // the longest time we are expecting a response
|
Timeout uint64 // the longest time we are expecting a response
|
||||||
timeout *time.Time // [not serialied]}
|
timeout *time.Time // [not serialied]
|
||||||
from *peer //
|
from *peer //
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,7 +165,9 @@ type peerAddr struct {
|
||||||
|
|
||||||
// peerAddr pretty prints as enode
|
// peerAddr pretty prints as enode
|
||||||
func (self peerAddr) String() string {
|
func (self peerAddr) String() string {
|
||||||
return fmt.Sprintf("enode://%x@%v:%d", self.ID, self.IP, self.Port)
|
var nodeid discover.NodeID
|
||||||
|
copy(nodeid[:], self.ID)
|
||||||
|
return discover.NewNode(nodeid, self.IP, 0, self.Port).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -231,7 +238,7 @@ peer/protocol instance when the node is registered by hive as online{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type syncRequestMsgData struct {
|
type syncRequestMsgData struct {
|
||||||
SyncState *syncState `rlp:"nil"`
|
SyncState *syncState `rlp:"nil" `
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *syncRequestMsgData) String() string {
|
func (self *syncRequestMsgData) String() string {
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,35 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
/*
|
/*
|
||||||
BZZ implements the bzz wire protocol of swarm
|
bzz implements the swarm wire protocol [bzz] (sister of eth and shh)
|
||||||
the protocol instance is launched on each peer by the network layer if the
|
the protocol instance is launched on each peer by the network layer if the
|
||||||
BZZ protocol handler is registered on the p2p server.
|
bzz protocol handler is registered on the p2p server.
|
||||||
|
|
||||||
The protocol takes care of actually communicating the bzz protocol
|
The bzz protocol component speaks the bzz protocol
|
||||||
* encoding and decoding requests for storage and retrieval
|
* handle the protocol handshake
|
||||||
* handling the s§protocol handshake
|
* register peers in the KΛÐΞMLIΛ table via the hive logistic manager
|
||||||
* dispaching to netstore for handling the DHT logic
|
* dispatch to hive for handling the DHT logic
|
||||||
* registering peers in the KΛÐΞMLIΛ table via the hive logistic manager
|
* encode and decode requests for storage and retrieval
|
||||||
* handling sync protocol messages via the syncer
|
* handle sync protocol messages via the syncer
|
||||||
* talks the SWAP payent protocol (swap accounting is done within NetStore)
|
* talks the SWAP payment protocol (swap accounting is done within NetStore)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
bzzswap "github.com/ethereum/go-ethereum/swarm/services/swap"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
|
||||||
"github.com/ethereum/go-ethereum/common/chequebook"
|
|
||||||
"github.com/ethereum/go-ethereum/common/swap"
|
|
||||||
"github.com/ethereum/go-ethereum/errs"
|
"github.com/ethereum/go-ethereum/errs"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/chequebook"
|
||||||
|
bzzswap "github.com/ethereum/go-ethereum/swarm/services/swap"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/swap/swap"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -67,21 +69,22 @@ type bzz struct {
|
||||||
selfID discover.NodeID // peer's node id used in peer advertising in handshake
|
selfID discover.NodeID // peer's node id used in peer advertising in handshake
|
||||||
key storage.Key // baseaddress as storage.Key
|
key storage.Key // baseaddress as storage.Key
|
||||||
storage StorageHandler // handler storage/retrieval related requests coming via the bzz wire protocol
|
storage StorageHandler // handler storage/retrieval related requests coming via the bzz wire protocol
|
||||||
hive *Hive // the logistic manager, peerPool, routing servicec and peer handler
|
hive *Hive // the logistic manager, peerPool, routing service and peer handler
|
||||||
dbAccess *DbAccess // access to db storage counter and iterator for syncing
|
dbAccess *DbAccess // access to db storage counter and iterator for syncing
|
||||||
requestDb *storage.LDBDatabase // db to persist backlog of deliveries to aid syncing
|
requestDb *storage.LDBDatabase // db to persist backlog of deliveries to aid syncing
|
||||||
remoteAddr *peerAddr // remote peers address
|
remoteAddr *peerAddr // remote peers address
|
||||||
peer *p2p.Peer // the p2p peer object
|
peer *p2p.Peer // the p2p peer object
|
||||||
rw p2p.MsgReadWriter // messageReadWriter to send messages to
|
rw p2p.MsgReadWriter // messageReadWriter to send messages to
|
||||||
errors *errs.Errors // errors table
|
errors *errs.Errors // errors table
|
||||||
|
backend bind.Backend
|
||||||
|
|
||||||
swap *swap.Swap // swap instance for the peer connection
|
swap *swap.Swap // swap instance for the peer connection
|
||||||
swapParams *bzzswap.SwapParams // swap settings both local and remote
|
swapParams *bzzswap.SwapParams // swap settings both local and remote
|
||||||
swapEnabled bool // flag to switch off SWAP (will be via Caps in handshake)
|
swapEnabled bool // flag to enable SWAP (will be set via Caps in handshake)
|
||||||
|
syncEnabled bool // flag to enable SYNC (will be set via Caps in handshake)
|
||||||
syncer *syncer // syncer instance for the peer connection
|
syncer *syncer // syncer instance for the peer connection
|
||||||
syncParams *SyncParams // syncer params
|
syncParams *SyncParams // syncer params
|
||||||
syncState *syncState // outgoing syncronisation state (contains reference to remote peers db counter)
|
syncState *syncState // outgoing syncronisation state (contains reference to remote peers db counter)
|
||||||
syncEnabled bool // flag to enable syncing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface type for handler of storage/retrieval related requests coming
|
// interface type for handler of storage/retrieval related requests coming
|
||||||
|
|
@ -105,7 +108,7 @@ on each peer connection
|
||||||
The Run function of the Bzz protocol class creates a bzz instance
|
The Run function of the Bzz protocol class creates a bzz instance
|
||||||
which will represent the peer for the swarm hive and all peer-aware components
|
which will represent the peer for the swarm hive and all peer-aware components
|
||||||
*/
|
*/
|
||||||
func Bzz(cloud StorageHandler, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams) (p2p.Protocol, error) {
|
func Bzz(cloud StorageHandler, backend bind.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams) (p2p.Protocol, error) {
|
||||||
|
|
||||||
// a single global request db is created for all peer connections
|
// a single global request db is created for all peer connections
|
||||||
// this is to persist delivery backlog and aid syncronisation
|
// this is to persist delivery backlog and aid syncronisation
|
||||||
|
|
@ -119,7 +122,7 @@ func Bzz(cloud StorageHandler, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapP
|
||||||
Version: Version,
|
Version: Version,
|
||||||
Length: ProtocolLength,
|
Length: ProtocolLength,
|
||||||
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
return run(requestDb, cloud, hive, dbaccess, sp, sy, p, rw)
|
return run(requestDb, cloud, backend, hive, dbaccess, sp, sy, p, rw)
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -136,10 +139,11 @@ the main protocol loop that
|
||||||
* whenever the loop terminates, the peer will disconnect with Subprotocol error
|
* whenever the loop terminates, the peer will disconnect with Subprotocol error
|
||||||
* whenever handlers return an error the loop terminates
|
* whenever handlers return an error the loop terminates
|
||||||
*/
|
*/
|
||||||
func run(requestDb *storage.LDBDatabase, depo StorageHandler, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
|
func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend bind.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
|
||||||
|
|
||||||
self := &bzz{
|
self := &bzz{
|
||||||
storage: depo,
|
storage: depo,
|
||||||
|
backend: backend,
|
||||||
hive: hive,
|
hive: hive,
|
||||||
dbAccess: dbaccess,
|
dbAccess: dbaccess,
|
||||||
requestDb: requestDb,
|
requestDb: requestDb,
|
||||||
|
|
@ -151,7 +155,7 @@ func run(requestDb *storage.LDBDatabase, depo StorageHandler, hive *Hive, dbacce
|
||||||
},
|
},
|
||||||
swapParams: sp,
|
swapParams: sp,
|
||||||
syncParams: sy,
|
syncParams: sy,
|
||||||
swapEnabled: true,
|
swapEnabled: hive.swapEnabled,
|
||||||
syncEnabled: true,
|
syncEnabled: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,6 +178,10 @@ func run(requestDb *storage.LDBDatabase, depo StorageHandler, hive *Hive, dbacce
|
||||||
|
|
||||||
// the main forever loop that handles incoming requests
|
// the main forever loop that handles incoming requests
|
||||||
for {
|
for {
|
||||||
|
if self.hive.blockRead {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
err = self.handle()
|
err = self.handle()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -182,7 +190,7 @@ func run(requestDb *storage.LDBDatabase, depo StorageHandler, hive *Hive, dbacce
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// may need to implement protocol drop only? don't want to kick off the peer
|
// TODO: may need to implement protocol drop only? don't want to kick off the peer
|
||||||
// if they are useful for other protocols
|
// if they are useful for other protocols
|
||||||
func (self *bzz) Drop() {
|
func (self *bzz) Drop() {
|
||||||
self.peer.Disconnect(p2p.DiscSubprotocolError)
|
self.peer.Disconnect(p2p.DiscSubprotocolError)
|
||||||
|
|
@ -213,7 +221,7 @@ func (self *bzz) handle() error {
|
||||||
// store requests are dispatched to netStore
|
// store requests are dispatched to netStore
|
||||||
var req storeRequestMsgData
|
var req storeRequestMsgData
|
||||||
if err := msg.Decode(&req); err != nil {
|
if err := msg.Decode(&req); err != nil {
|
||||||
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] incoming store request: %s", req.String())
|
glog.V(logger.Debug).Infof("[BZZ] incoming store request: %s", req.String())
|
||||||
// swap accounting is done within forwarding
|
// swap accounting is done within forwarding
|
||||||
|
|
@ -223,7 +231,7 @@ func (self *bzz) handle() error {
|
||||||
// retrieve Requests are dispatched to netStore
|
// retrieve Requests are dispatched to netStore
|
||||||
var req retrieveRequestMsgData
|
var req retrieveRequestMsgData
|
||||||
if err := msg.Decode(&req); err != nil {
|
if err := msg.Decode(&req); err != nil {
|
||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
req.from = &peer{bzz: self}
|
req.from = &peer{bzz: self}
|
||||||
// if request is lookup and not to be delivered
|
// if request is lookup and not to be delivered
|
||||||
|
|
@ -243,55 +251,55 @@ func (self *bzz) handle() error {
|
||||||
// dispatches new peer data to the hive that adds them to KADDB
|
// dispatches new peer data to the hive that adds them to KADDB
|
||||||
var req peersMsgData
|
var req peersMsgData
|
||||||
if err := msg.Decode(&req); err != nil {
|
if err := msg.Decode(&req); err != nil {
|
||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
req.from = &peer{bzz: self}
|
req.from = &peer{bzz: self}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] incoming peer addresses: %v", req)
|
glog.V(logger.Debug).Infof("[BZZ] <- peer addresses: %v", req)
|
||||||
self.hive.HandlePeersMsg(&req, &peer{bzz: self})
|
self.hive.HandlePeersMsg(&req, &peer{bzz: self})
|
||||||
|
|
||||||
case syncRequestMsg:
|
case syncRequestMsg:
|
||||||
var req syncRequestMsgData
|
var req syncRequestMsgData
|
||||||
if err := msg.Decode(&req); err != nil {
|
if err := msg.Decode(&req); err != nil {
|
||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] sync request received: %v", req)
|
glog.V(logger.Debug).Infof("[BZZ] <- sync request: %v", req)
|
||||||
self.sync(req.SyncState)
|
self.sync(req.SyncState)
|
||||||
|
|
||||||
case unsyncedKeysMsg:
|
case unsyncedKeysMsg:
|
||||||
// coming from parent node offering
|
// coming from parent node offering
|
||||||
var req unsyncedKeysMsgData
|
var req unsyncedKeysMsgData
|
||||||
if err := msg.Decode(&req); err != nil {
|
if err := msg.Decode(&req); err != nil {
|
||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] incoming unsynced keys msg: %s", req.String())
|
glog.V(logger.Debug).Infof("[BZZ] <- unsynced keys : %s", req.String())
|
||||||
err := self.storage.HandleUnsyncedKeysMsg(&req, &peer{bzz: self})
|
err := self.storage.HandleUnsyncedKeysMsg(&req, &peer{bzz: self})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
// set peers state to persist
|
|
||||||
self.syncState = req.State
|
|
||||||
|
|
||||||
case deliveryRequestMsg:
|
case deliveryRequestMsg:
|
||||||
// response to syncKeysMsg hashes filtered not existing in db
|
// response to syncKeysMsg hashes filtered not existing in db
|
||||||
// also relays the last synced state to the source
|
// also relays the last synced state to the source
|
||||||
var req deliveryRequestMsgData
|
var req deliveryRequestMsgData
|
||||||
if err := msg.Decode(&req); err != nil {
|
if err := msg.Decode(&req); err != nil {
|
||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "<-msg %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] incoming delivery request: %s", req.String())
|
glog.V(logger.Debug).Infof("[BZZ] <- delivery request: %s", req.String())
|
||||||
err := self.storage.HandleDeliveryRequestMsg(&req, &peer{bzz: self})
|
err := self.storage.HandleDeliveryRequestMsg(&req, &peer{bzz: self})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
case paymentMsg:
|
case paymentMsg:
|
||||||
// swap protocol message for payment, Units paid for, Cheque paid with
|
// swap protocol message for payment, Units paid for, Cheque paid with
|
||||||
var req paymentMsgData
|
if self.swapEnabled {
|
||||||
if err := msg.Decode(&req); err != nil {
|
var req paymentMsgData
|
||||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
if err := msg.Decode(&req); err != nil {
|
||||||
|
return self.protoError(ErrDecode, "<- %v: %v", msg, err)
|
||||||
|
}
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] <- payment: %s", req.String())
|
||||||
|
self.swap.Receive(int(req.Units), req.Promise)
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] incoming payment: %s", req.String())
|
|
||||||
self.swap.Receive(int(req.Units), req.Promise)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// no other message is allowed
|
// no other message is allowed
|
||||||
|
|
@ -335,7 +343,7 @@ func (self *bzz) handleStatus() (err error) {
|
||||||
|
|
||||||
var status statusMsgData
|
var status statusMsgData
|
||||||
if err := msg.Decode(&status); err != nil {
|
if err := msg.Decode(&status); err != nil {
|
||||||
return self.protoError(ErrDecode, "msg %v: %v", msg, err)
|
return self.protoError(ErrDecode, " %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.NetworkId != NetworkId {
|
if status.NetworkId != NetworkId {
|
||||||
|
|
@ -351,7 +359,7 @@ func (self *bzz) handleStatus() (err error) {
|
||||||
|
|
||||||
if self.swapEnabled {
|
if self.swapEnabled {
|
||||||
// set remote profile for accounting
|
// set remote profile for accounting
|
||||||
self.swap, err = bzzswap.NewSwap(self.swapParams, status.Swap, self)
|
self.swap, err = bzzswap.NewSwap(self.swapParams, status.Swap, self.backend, self)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.protoError(ErrSwap, "%v", err)
|
return self.protoError(ErrSwap, "%v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -361,10 +369,9 @@ func (self *bzz) handleStatus() (err error) {
|
||||||
self.hive.addPeer(&peer{bzz: self})
|
self.hive.addPeer(&peer{bzz: self})
|
||||||
|
|
||||||
// hive sets syncstate so sync should start after node added
|
// hive sets syncstate so sync should start after node added
|
||||||
if self.syncEnabled {
|
glog.V(logger.Info).Infof("[BZZ] syncronisation request sent with %v", self.syncState)
|
||||||
glog.V(logger.Info).Infof("[BZZ] syncronisation request sent with %v", self.syncState)
|
self.syncRequest()
|
||||||
self.syncRequest()
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,9 +384,12 @@ func (self *bzz) sync(state *syncState) error {
|
||||||
cnt := self.dbAccess.counter()
|
cnt := self.dbAccess.counter()
|
||||||
remoteaddr := self.remoteAddr.Addr
|
remoteaddr := self.remoteAddr.Addr
|
||||||
start, stop := self.hive.kad.KeyRange(remoteaddr)
|
start, stop := self.hive.kad.KeyRange(remoteaddr)
|
||||||
|
|
||||||
|
// an explicitly received nil syncstate disables syncronisation
|
||||||
if state == nil {
|
if state == nil {
|
||||||
state = newSyncState(start, stop, cnt)
|
self.syncEnabled = false
|
||||||
glog.V(logger.Warn).Infof("[BZZ] peer %08x provided no sync state, setting up full sync: %v\n", remoteaddr[:4], state)
|
glog.V(logger.Warn).Infof("[BZZ] syncronisation disabled for peer %v", self)
|
||||||
|
state = &syncState{DbSyncState: &storage.DbSyncState{}, Synced: true}
|
||||||
} else {
|
} else {
|
||||||
state.synced = make(chan bool)
|
state.synced = make(chan bool)
|
||||||
state.SessionAt = cnt
|
state.SessionAt = cnt
|
||||||
|
|
@ -387,6 +397,7 @@ func (self *bzz) sync(state *syncState) error {
|
||||||
state.Start = storage.Key(start[:])
|
state.Start = storage.Key(start[:])
|
||||||
state.Stop = storage.Key(stop[:])
|
state.Stop = storage.Key(stop[:])
|
||||||
}
|
}
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] syncronisation requested by peer %v at state %v", self, state)
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
self.syncer, err = newSyncer(
|
self.syncer, err = newSyncer(
|
||||||
|
|
@ -394,11 +405,12 @@ func (self *bzz) sync(state *syncState) error {
|
||||||
storage.Key(remoteaddr[:]),
|
storage.Key(remoteaddr[:]),
|
||||||
self.dbAccess,
|
self.dbAccess,
|
||||||
self.unsyncedKeys, self.store,
|
self.unsyncedKeys, self.store,
|
||||||
self.syncParams, state,
|
self.syncParams, state, func() bool { return self.syncEnabled },
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return self.protoError(ErrSync, "%v", err)
|
return self.protoError(ErrSync, "%v", err)
|
||||||
}
|
}
|
||||||
|
glog.V(logger.Detail).Infof("[BZZ] syncer set for peer %v", self)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,8 +455,13 @@ func (self *bzz) store(req *storeRequestMsgData) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *bzz) syncRequest() error {
|
func (self *bzz) syncRequest() error {
|
||||||
req := &syncRequestMsgData{
|
req := &syncRequestMsgData{}
|
||||||
SyncState: self.syncState,
|
if self.hive.syncEnabled {
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] syncronisation request to peer %v at state %v", self, self.syncState)
|
||||||
|
req.SyncState = self.syncState
|
||||||
|
}
|
||||||
|
if self.syncState == nil {
|
||||||
|
glog.V(logger.Warn).Infof("[BZZ] syncronisation disabled for peer %v at state %v", self, self.syncState)
|
||||||
}
|
}
|
||||||
return self.send(syncRequestMsg, req)
|
return self.send(syncRequestMsg, req)
|
||||||
}
|
}
|
||||||
|
|
@ -496,7 +513,11 @@ func (self *bzz) protoErrorDisconnect(err *errs.Error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *bzz) send(msg uint64, data interface{}) error {
|
func (self *bzz) send(msg uint64, data interface{}) error {
|
||||||
glog.V(logger.Debug).Infof("[BZZ] -> %v: %v (%T) to %v", msg, data, data, self)
|
if self.hive.blockWrite {
|
||||||
|
return fmt.Errorf("network write blocked")
|
||||||
|
}
|
||||||
|
// self.messages = append(self.messages, "")
|
||||||
|
glog.V(logger.Detail).Infof("[BZZ] -> %v: %v (%T) to %v", msg, data, data, self)
|
||||||
err := p2p.Send(self.rw, msg, data)
|
err := p2p.Send(self.rw, msg, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.Drop()
|
self.Drop()
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ func newSyncDb(db *storage.LDBDatabase, key storage.Key, priority uint, bufferSi
|
||||||
batch: make(chan chan int),
|
batch: make(chan chan int),
|
||||||
dbBatchSize: dbBatchSize,
|
dbBatchSize: dbBatchSize,
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] syncDb[peer: %v, priority: %v] - initialised", key.Log(), priority)
|
glog.V(logger.Detail).Infof("[BZZ] syncDb[peer: %v, priority: %v] - initialised", key.Log(), priority)
|
||||||
|
|
||||||
// starts the main forever loop reading from buffer
|
// starts the main forever loop reading from buffer
|
||||||
go syncdb.bufferRead(deliver)
|
go syncdb.bufferRead(deliver)
|
||||||
|
|
@ -159,14 +159,13 @@ LOOP:
|
||||||
}
|
}
|
||||||
self.dbTotal++
|
self.dbTotal++
|
||||||
self.total++
|
self.total++
|
||||||
// otherwise break after select
|
// otherwise break after selec
|
||||||
|
|
||||||
case dbSize = <-self.batch:
|
case dbSize = <-self.batch:
|
||||||
// explicit request for batch
|
// explicit request for batch
|
||||||
if inBatch == 0 && quit != nil {
|
if inBatch == 0 && quit != nil {
|
||||||
// there was no writes since the last batch so db depleted
|
// there was no writes since the last batch so db depleted
|
||||||
// switch to buffer mode
|
// switch to buffer mode
|
||||||
glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] empty db: switching to buffer", self.priority)
|
glog.V(logger.Debug).Infof("[BZZ] syncDb[%v] empty db: switching to buffer", self.priority)
|
||||||
db = nil
|
db = nil
|
||||||
buffer = self.buffer
|
buffer = self.buffer
|
||||||
dbSize <- 0 // indicates to 'caller' that batch has been written
|
dbSize <- 0 // indicates to 'caller' that batch has been written
|
||||||
|
|
@ -175,9 +174,9 @@ LOOP:
|
||||||
}
|
}
|
||||||
binary.BigEndian.PutUint64(counterValue, counter)
|
binary.BigEndian.PutUint64(counterValue, counter)
|
||||||
batch.Put(self.counterKey, counterValue)
|
batch.Put(self.counterKey, counterValue)
|
||||||
glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] write batch %v/%v - %x - %x", self.priority, inBatch, counter, self.counterKey, counterValue)
|
glog.V(logger.Debug).Infof("[BZZ] syncDb[%v] write batch %v/%v - %x - %x", self.priority, inBatch, counter, self.counterKey, counterValue)
|
||||||
batch = self.writeSyncBatch(batch)
|
batch = self.writeSyncBatch(batch)
|
||||||
dbSize <- inBatch
|
dbSize <- inBatch // indicates to 'caller' that batch has been written
|
||||||
inBatch = 0
|
inBatch = 0
|
||||||
continue LOOP
|
continue LOOP
|
||||||
|
|
||||||
|
|
@ -222,7 +221,7 @@ LOOP:
|
||||||
func (self *syncDb) writeSyncBatch(batch *leveldb.Batch) *leveldb.Batch {
|
func (self *syncDb) writeSyncBatch(batch *leveldb.Batch) *leveldb.Batch {
|
||||||
err := self.db.Write(batch)
|
err := self.db.Write(batch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Detail).Infof("[BZZ] syncDb[%v] saving batch to db failed: %v", self.priority, err)
|
glog.V(logger.Warn).Infof("[BZZ] syncDb[%v] saving batch to db failed: %v", self.priority, err)
|
||||||
return batch
|
return batch
|
||||||
}
|
}
|
||||||
return new(leveldb.Batch)
|
return new(leveldb.Batch)
|
||||||
|
|
@ -275,7 +274,11 @@ func (self *syncDb) dbRead(useBatches bool, counter uint64, fun func(interface{}
|
||||||
// this could be called before all cnt items sent out
|
// this could be called before all cnt items sent out
|
||||||
// so that loop is not blocking while delivering
|
// so that loop is not blocking while delivering
|
||||||
// only relevant if cnt is large
|
// only relevant if cnt is large
|
||||||
self.batch <- batchSizes
|
select {
|
||||||
|
case self.batch <- batchSizes:
|
||||||
|
case <-self.quit:
|
||||||
|
return
|
||||||
|
}
|
||||||
// wait for the write to finish and get the item count in the next batch
|
// wait for the write to finish and get the item count in the next batch
|
||||||
cnt = <-batchSizes
|
cnt = <-batchSizes
|
||||||
batches++
|
batches++
|
||||||
|
|
@ -318,7 +321,7 @@ func (self *syncDb) dbRead(useBatches bool, counter uint64, fun func(interface{}
|
||||||
n++
|
n++
|
||||||
total++
|
total++
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("[BZZ] syncDb[%v/%v] - db session closed, batches: %v, total: %v, session total from db: %v/%v", self.key.Log(), self.priority, batches, total, self.dbTotal, self.total)
|
glog.V(logger.Debug).Infof("[BZZ] syncDb[%v/%v] - db session closed, batches: %v, total: %v, session total from db: %v/%v", self.key.Log(), self.priority, batches, total, self.dbTotal, self.total)
|
||||||
self.db.Write(del) // this could be async called only when db is idle
|
self.db.Write(del) // this could be async called only when db is idle
|
||||||
it.Release()
|
it.Release()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/kademlia"
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
|
|
@ -133,6 +132,7 @@ func NewSyncParams(bzzdir string) *SyncParams {
|
||||||
// syncer is the agent that manages content distribution/storage replication/chunk storeRequest forwarding
|
// syncer is the agent that manages content distribution/storage replication/chunk storeRequest forwarding
|
||||||
type syncer struct {
|
type syncer struct {
|
||||||
*SyncParams // sync parameters
|
*SyncParams // sync parameters
|
||||||
|
syncF func() bool // if syncing is needed
|
||||||
key storage.Key // remote peers address key
|
key storage.Key // remote peers address key
|
||||||
state *syncState // sync state for our dbStore
|
state *syncState // sync state for our dbStore
|
||||||
syncStates chan *syncState // different stages of sync
|
syncStates chan *syncState // different stages of sync
|
||||||
|
|
@ -165,6 +165,7 @@ func newSyncer(
|
||||||
store func(*storeRequestMsgData) error,
|
store func(*storeRequestMsgData) error,
|
||||||
params *SyncParams,
|
params *SyncParams,
|
||||||
state *syncState,
|
state *syncState,
|
||||||
|
syncF func() bool,
|
||||||
) (*syncer, error) {
|
) (*syncer, error) {
|
||||||
|
|
||||||
syncBufferSize := params.SyncBufferSize
|
syncBufferSize := params.SyncBufferSize
|
||||||
|
|
@ -172,6 +173,7 @@ func newSyncer(
|
||||||
dbBatchSize := params.RequestDbBatchSize
|
dbBatchSize := params.RequestDbBatchSize
|
||||||
|
|
||||||
self := &syncer{
|
self := &syncer{
|
||||||
|
syncF: syncF,
|
||||||
key: remotekey,
|
key: remotekey,
|
||||||
dbAccess: dbAccess,
|
dbAccess: dbAccess,
|
||||||
syncStates: make(chan *syncState, 20),
|
syncStates: make(chan *syncState, 20),
|
||||||
|
|
@ -191,35 +193,19 @@ func newSyncer(
|
||||||
// initialise a syncdb instance for each priority queue
|
// initialise a syncdb instance for each priority queue
|
||||||
self.queues[i] = newSyncDb(db, remotekey, uint(i), syncBufferSize, dbBatchSize, self.deliver(uint(i)))
|
self.queues[i] = newSyncDb(db, remotekey, uint(i), syncBufferSize, dbBatchSize, self.deliver(uint(i)))
|
||||||
}
|
}
|
||||||
self.state = state
|
|
||||||
glog.V(logger.Info).Infof("[BZZ] syncer started: %v", state)
|
glog.V(logger.Info).Infof("[BZZ] syncer started: %v", state)
|
||||||
// launch chunk delivery service
|
// launch chunk delivery service
|
||||||
go self.syncDeliveries()
|
go self.syncDeliveries()
|
||||||
// launch sync task manager
|
// launch sync task manager
|
||||||
go self.sync()
|
if self.syncF() {
|
||||||
|
go self.sync()
|
||||||
|
}
|
||||||
// process unsynced keys to broadcast
|
// process unsynced keys to broadcast
|
||||||
go self.syncUnsyncedKeys()
|
go self.syncUnsyncedKeys()
|
||||||
|
|
||||||
return self, nil
|
return self, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSyncState returns a default sync state given local and remote
|
|
||||||
// addresses and db count
|
|
||||||
func newSyncState(start, stop kademlia.Address, count uint64) *syncState {
|
|
||||||
// -> (] keyrange for db iterator -- interval open from the left
|
|
||||||
// storage count range --- interval open from the right
|
|
||||||
return &syncState{
|
|
||||||
DbSyncState: &storage.DbSyncState{
|
|
||||||
Start: storage.Key(start[:]),
|
|
||||||
Stop: storage.Key(stop[:]),
|
|
||||||
First: 0,
|
|
||||||
Last: count,
|
|
||||||
},
|
|
||||||
SessionAt: count,
|
|
||||||
synced: make(chan bool),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// metadata serialisation
|
// metadata serialisation
|
||||||
func encodeSync(state *syncState) (*json.RawMessage, error) {
|
func encodeSync(state *syncState) (*json.RawMessage, error) {
|
||||||
data, err := json.MarshalIndent(state, "", " ")
|
data, err := json.MarshalIndent(state, "", " ")
|
||||||
|
|
@ -238,7 +224,7 @@ func decodeSync(meta *json.RawMessage) (*syncState, error) {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil, fmt.Errorf("unable to deserialise sync state from <nil>")
|
return nil, fmt.Errorf("unable to deserialise sync state from <nil>")
|
||||||
}
|
}
|
||||||
state := newSyncState(kademlia.Address{}, kademlia.Address{}, 0)
|
state := &syncState{DbSyncState: &storage.DbSyncState{}}
|
||||||
err := json.Unmarshal(data, state)
|
err := json.Unmarshal(data, state)
|
||||||
return state, err
|
return state, err
|
||||||
}
|
}
|
||||||
|
|
@ -330,7 +316,7 @@ func (self *syncer) syncState(state *syncState) {
|
||||||
// stop quits both request processor and saves the request cache to disk
|
// stop quits both request processor and saves the request cache to disk
|
||||||
func (self *syncer) stop() {
|
func (self *syncer) stop() {
|
||||||
close(self.quit)
|
close(self.quit)
|
||||||
glog.V(logger.Debug).Infof("[BZZ] syncer[%v]: save sync request db backlog", self.key.Log())
|
glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: stopand save sync request db backlog", self.key.Log())
|
||||||
for _, db := range self.queues {
|
for _, db := range self.queues {
|
||||||
db.stop()
|
db.stop()
|
||||||
}
|
}
|
||||||
|
|
@ -478,6 +464,7 @@ LOOP:
|
||||||
glog.V(logger.Warn).Infof("[BZZ] syncer[%v]: unable to send unsynced keys: %v", err)
|
glog.V(logger.Warn).Infof("[BZZ] syncer[%v]: unable to send unsynced keys: %v", err)
|
||||||
}
|
}
|
||||||
unsynced = nil
|
unsynced = nil
|
||||||
|
keys = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// process item and add it to the batch
|
// process item and add it to the batch
|
||||||
|
|
@ -503,11 +490,11 @@ LOOP:
|
||||||
deliveryRequest = nil
|
deliveryRequest = nil
|
||||||
|
|
||||||
case <-newUnsyncedKeys:
|
case <-newUnsyncedKeys:
|
||||||
glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: new unsynked keys available", self.key.Log())
|
glog.V(logger.Detail).Infof("[BZZ] syncer[%v]: new unsynced keys available", self.key.Log())
|
||||||
// this 1 cap channel can wake up the loop
|
// this 1 cap channel can wake up the loop
|
||||||
// signals that data is available to send if peer is ready to receive
|
// signals that data is available to send if peer is ready to receive
|
||||||
newUnsyncedKeys = nil
|
newUnsyncedKeys = nil
|
||||||
// this can only happen if keys is
|
keys = self.keys[High]
|
||||||
|
|
||||||
case state, more = <-syncStates:
|
case state, more = <-syncStates:
|
||||||
// this resets the state
|
// this resets the state
|
||||||
|
|
@ -620,13 +607,16 @@ func (self *syncer) syncDeliveries() {
|
||||||
If sync mode is off then, requests are directly sent to deliveries
|
If sync mode is off then, requests are directly sent to deliveries
|
||||||
*/
|
*/
|
||||||
func (self *syncer) addRequest(req interface{}, ty int) {
|
func (self *syncer) addRequest(req interface{}, ty int) {
|
||||||
// retrieve priority for request type
|
// retrieve priority for request type name int8
|
||||||
|
|
||||||
priority := self.SyncPriorities[ty]
|
priority := self.SyncPriorities[ty]
|
||||||
// sync mode for this type ON
|
// sync mode for this type ON
|
||||||
if self.SyncModes[ty] {
|
if self.syncF() || ty == DeliverReq {
|
||||||
self.addKey(req, priority, self.quit)
|
if self.SyncModes[ty] {
|
||||||
} else {
|
self.addKey(req, priority, self.quit)
|
||||||
self.addDelivery(req, priority, self.quit)
|
} else {
|
||||||
|
self.addDelivery(req, priority, self.quit)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
52
swarm/services/chequebook/api.go
Normal file
52
swarm/services/chequebook/api.go
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
package chequebook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Version = "1.0"
|
||||||
|
|
||||||
|
var errNoChequebook = errors.New("no chequebook")
|
||||||
|
|
||||||
|
type Api struct {
|
||||||
|
chequebookf func() *Chequebook
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApi(ch func() *Chequebook) *Api {
|
||||||
|
return &Api{ch}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Api) Balance() (string, error) {
|
||||||
|
ch := self.chequebookf()
|
||||||
|
if ch == nil {
|
||||||
|
return "", errNoChequebook
|
||||||
|
}
|
||||||
|
return ch.Balance().String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Api) Issue(beneficiary common.Address, amount *big.Int) (cheque *Cheque, err error) {
|
||||||
|
ch := self.chequebookf()
|
||||||
|
if ch == nil {
|
||||||
|
return nil, errNoChequebook
|
||||||
|
}
|
||||||
|
return ch.Issue(beneficiary, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Api) Cash(cheque *Cheque) (txhash string, err error) {
|
||||||
|
ch := self.chequebookf()
|
||||||
|
if ch == nil {
|
||||||
|
return "", errNoChequebook
|
||||||
|
}
|
||||||
|
return ch.Cash(cheque)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Api) Deposit(amount *big.Int) (txhash string, err error) {
|
||||||
|
ch := self.chequebookf()
|
||||||
|
if ch == nil {
|
||||||
|
return "", errNoChequebook
|
||||||
|
}
|
||||||
|
return ch.Deposit(amount)
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:generate abigen --sol contract/chequebook.sol --pkg contract --out contract/jaak.go
|
||||||
package chequebook
|
package chequebook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -11,14 +12,20 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/swap"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/chequebook/contract"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/swap/swap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// func init() {
|
||||||
|
// glog.SetToStderr(true)
|
||||||
|
// glog.SetV(6)
|
||||||
|
// }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Chequebook package is a go API to the 'chequebook' ethereum smart contract
|
Chequebook package is a go API to the 'chequebook' ethereum smart contract
|
||||||
With convenience methods that allow using chequebook for
|
With convenience methods that allow using chequebook for
|
||||||
|
|
@ -38,24 +45,11 @@ Some functionality require interacting with the blockchain:
|
||||||
Backend is the interface for that
|
Backend is the interface for that
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const (
|
var (
|
||||||
gasToCash = "2000000" // gas cost of a cash transaction using chequebook
|
gasToCash = big.NewInt(2000000) // gas cost of a cash transaction using chequebook
|
||||||
getSentAbiPre = "d75d691d" // sent amount accessor in the chequebook contract
|
// gasToDeploy = big.NewInt(3000000)
|
||||||
cashAbiPre = "fbf788d6" // abi preamble signature for cash method of the chequebook
|
|
||||||
queryInterval = 15000000000 // 15 seconds
|
|
||||||
deployGas = "3000000"
|
|
||||||
// confirmationInterval = 3 * 10 * *11 // 5 minutes
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Backend is the interface to interact with the Ethereum blockchain
|
|
||||||
// implemented by xeth.XEth
|
|
||||||
type Backend interface {
|
|
||||||
Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
|
|
||||||
Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error)
|
|
||||||
GetTxReceipt(txhash common.Hash) *types.Receipt
|
|
||||||
CodeAt(address string) string
|
|
||||||
}
|
|
||||||
|
|
||||||
// rlp serialised cheque model for use with the chequebook
|
// rlp serialised cheque model for use with the chequebook
|
||||||
type Cheque struct {
|
type Cheque struct {
|
||||||
// the address of the contract itself needed to avoid cross-contract submission
|
// the address of the contract itself needed to avoid cross-contract submission
|
||||||
|
|
@ -65,113 +59,84 @@ type Cheque struct {
|
||||||
Sig []byte // signature Sign(Sha3(contract, beneficiary, amount), prvKey)
|
Sig []byte // signature Sign(Sha3(contract, beneficiary, amount), prvKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Params struct {
|
|
||||||
ContractCode, ContractAbi, ContractSource string
|
|
||||||
}
|
|
||||||
|
|
||||||
var ContractParams = &Params{ContractCode, ContractAbi, ContractSource}
|
|
||||||
|
|
||||||
func (self *Cheque) String() string {
|
func (self *Cheque) String() string {
|
||||||
return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", self.Contract.Hex(), self.Beneficiary.Hex(), self.Amount, self.Sig)
|
return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", self.Contract.Hex(), self.Beneficiary.Hex(), self.Amount, self.Sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Params struct {
|
||||||
|
ContractCode, ContractAbi string
|
||||||
|
}
|
||||||
|
|
||||||
|
var ContractParams = &Params{contract.ChequebookBin, contract.ChequebookABI}
|
||||||
|
|
||||||
// chequebook to create, sign cheques from single contract to multiple beneficiarys
|
// chequebook to create, sign cheques from single contract to multiple beneficiarys
|
||||||
// outgoing payment handler for peer to peer micropayments
|
// outgoing payment handler for peer to peer micropayments
|
||||||
type Chequebook struct {
|
type Chequebook struct {
|
||||||
path string // path to chequebook file
|
path string // path to chequebook file
|
||||||
prvKey *ecdsa.PrivateKey // private key to sign cheque with
|
prvKey *ecdsa.PrivateKey // private key to sign cheque with
|
||||||
lock sync.Mutex //
|
lock sync.Mutex //
|
||||||
backend Backend // blockchain API
|
backend bind.Backend // blockchain API
|
||||||
quit chan bool // when closed causes autodeposit to stop
|
// abigen bind.ContractBackend // blockchain contract backend for abigen
|
||||||
owner common.Address // owner address (derived from pubkey)
|
quit chan bool // when closed causes autodeposit to stop
|
||||||
|
owner common.Address // owner address (derived from pubkey)
|
||||||
|
contract *contract.Chequebook // abigen binding
|
||||||
|
session *contract.ChequebookSession // abigen binding with Tx Opts
|
||||||
|
|
||||||
// persisted fields
|
// persisted fields
|
||||||
balance *big.Int // not synced with blockchain
|
balance *big.Int // not synced with blockchain
|
||||||
contract common.Address // contract address
|
contractAddr common.Address // contract address
|
||||||
sent map[common.Address]*big.Int //tallies for beneficiarys
|
sent map[common.Address]*big.Int //tallies for beneficiarys
|
||||||
|
|
||||||
txhash string // tx hash of last deposit tx
|
txhash string // tx hash of last deposit tx
|
||||||
threshold *big.Int // threshold that triggers autodeposit if not nil
|
threshold *big.Int // threshold that triggers autodeposit if not nil
|
||||||
buffer *big.Int // buffer to keep on top of balance for fork protection
|
buffer *big.Int // buffer to keep on top of balance for fork protection
|
||||||
}
|
}
|
||||||
|
|
||||||
func Deploy(owner common.Address, backend Backend, amount *big.Int, confirmationInterval, timeout time.Duration) (contract common.Address, err error) {
|
func (self *Chequebook) String() string {
|
||||||
if (owner == common.Address{}) {
|
return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey)
|
||||||
return contract, fmt.Errorf("invalid owner %v. Owner needed to deploy chequebook contract: %v", owner.Hex(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
txhash, err := backend.Transact(owner.Hex(), "", "", amount.String(), deployGas, "", ContractCode)
|
|
||||||
if err != nil {
|
|
||||||
return contract, fmt.Errorf("unable to send chequebook creation transaction: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
timer := time.NewTimer(timeout).C
|
|
||||||
ticker := time.NewTicker(queryInterval).C
|
|
||||||
OUT:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
|
|
||||||
case <-timer:
|
|
||||||
if ticker == nil {
|
|
||||||
// ticker is nil, receipt was found and confirmation interval passed
|
|
||||||
err = Validate(contract, backend)
|
|
||||||
if err != nil {
|
|
||||||
return contract, fmt.Errorf("invalid contract at %v after %v: %v", contract.Hex(), confirmationInterval, err)
|
|
||||||
}
|
|
||||||
break OUT
|
|
||||||
}
|
|
||||||
// ticker is non-nil meaning receipt not found yet
|
|
||||||
return contract, fmt.Errorf("chequebook deployment timed out in %v", timeout)
|
|
||||||
|
|
||||||
case <-ticker:
|
|
||||||
receipt := backend.GetTxReceipt(common.HexToHash(txhash))
|
|
||||||
if receipt != nil {
|
|
||||||
contract = receipt.ContractAddress
|
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] chequebook deployed at %v (owner: %v)", contract.Hex(), owner.Hex())
|
|
||||||
timer = time.NewTimer(confirmationInterval).C
|
|
||||||
ticker = nil
|
|
||||||
} else {
|
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] check if chequebook deployed (txhash: %v)", txhash)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return contract, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Validate(contract common.Address, backend Backend) (err error) {
|
// NewChequebook(path, contract, prvKey, abibbackend, backend) creates a new Chequebook
|
||||||
if (contract == common.Address{}) {
|
func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend bind.Backend) (self *Chequebook, err error) {
|
||||||
return fmt.Errorf("zero address")
|
|
||||||
}
|
|
||||||
code := backend.CodeAt(contract.Hex())
|
|
||||||
if code != ContractDeployedCode {
|
|
||||||
return fmt.Errorf("incorrect code %v:\n%v\n%v", contract.Hex(), code, ContractDeployedCode)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewChequebook(path, contract, balance, prvKey) creates a new Chequebook
|
|
||||||
func NewChequebook(path string, contract common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) {
|
|
||||||
balance := new(big.Int)
|
balance := new(big.Int)
|
||||||
sent := make(map[common.Address]*big.Int)
|
sent := make(map[common.Address]*big.Int)
|
||||||
owner := crypto.PubkeyToAddress(prvKey.PublicKey)
|
|
||||||
self = &Chequebook{
|
chbook, err := contract.NewChequebook(contractAddr, backend)
|
||||||
balance: balance,
|
if err != nil {
|
||||||
contract: contract,
|
return nil, err
|
||||||
sent: sent,
|
|
||||||
path: path,
|
|
||||||
prvKey: prvKey,
|
|
||||||
backend: backend,
|
|
||||||
owner: owner,
|
|
||||||
}
|
}
|
||||||
if (contract != common.Address{}) {
|
transactOpts := bind.NewKeyedTransactor(prvKey)
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] new chequebook initialised from %v (owner: %v)", contract.Hex(), owner.Hex())
|
session := &contract.ChequebookSession{
|
||||||
|
Contract: chbook,
|
||||||
|
TransactOpts: *transactOpts,
|
||||||
|
}
|
||||||
|
|
||||||
|
self = &Chequebook{
|
||||||
|
prvKey: prvKey,
|
||||||
|
balance: balance,
|
||||||
|
contractAddr: contractAddr,
|
||||||
|
sent: sent,
|
||||||
|
path: path,
|
||||||
|
backend: backend,
|
||||||
|
owner: transactOpts.From,
|
||||||
|
contract: chbook,
|
||||||
|
session: session,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contractAddr != common.Address{}) {
|
||||||
|
self.setBalanceFromBlockChain()
|
||||||
|
glog.V(logger.Detail).Infof("[CHEQUEBOOK] new chequebook initialised from %v (owner: %v, balance: %s)", contractAddr, self.owner.Hex(), self.balance.String())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Chequebook) setBalanceFromBlockChain() {
|
||||||
|
balance := self.backend.BalanceAt(self.contractAddr)
|
||||||
|
self.balance.Set(balance)
|
||||||
|
}
|
||||||
|
|
||||||
// LoadChequebook(path, prvKey, backend) loads a chequebook from disk (file path)
|
// LoadChequebook(path, prvKey, backend) loads a chequebook from disk (file path)
|
||||||
func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) {
|
func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend bind.Backend, checkBalance bool) (self *Chequebook, err error) {
|
||||||
var data []byte
|
var data []byte
|
||||||
data, err = ioutil.ReadFile(path)
|
data, err = ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -184,7 +149,11 @@ func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend) (sel
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] loaded chequebook (%s, owner: %v) initialised from %v", self.contract.Hex(), self.owner.Hex(), path)
|
if checkBalance {
|
||||||
|
self.setBalanceFromBlockChain()
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(logger.Detail).Infof("[CHEQUEBOOK] loaded chequebook (%s, owner: %v, balance: %v) initialised from %v", self.contractAddr.Hex(), self.owner.Hex(), self.balance, path)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +176,7 @@ func (self *Chequebook) UnmarshalJSON(data []byte) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance)
|
return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance)
|
||||||
}
|
}
|
||||||
self.contract = common.HexToAddress(file.Contract)
|
self.contractAddr = common.HexToAddress(file.Contract)
|
||||||
for addr, sent := range file.Sent {
|
for addr, sent := range file.Sent {
|
||||||
self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10)
|
self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -220,7 +189,7 @@ func (self *Chequebook) UnmarshalJSON(data []byte) error {
|
||||||
func (self *Chequebook) MarshalJSON() ([]byte, error) {
|
func (self *Chequebook) MarshalJSON() ([]byte, error) {
|
||||||
var file = &chequebookFile{
|
var file = &chequebookFile{
|
||||||
Balance: self.balance.String(),
|
Balance: self.balance.String(),
|
||||||
Contract: self.contract.Hex(),
|
Contract: self.contractAddr.Hex(),
|
||||||
Owner: self.owner.Hex(),
|
Owner: self.owner.Hex(),
|
||||||
Sent: make(map[string]string),
|
Sent: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +207,7 @@ func (self *Chequebook) Save() (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] saving chequebook (%s) to %v", self.contract.Hex(), self.path)
|
glog.V(logger.Detail).Infof("[CHEQUEBOOK] saving chequebook (%s) to %v", self.contractAddr.Hex(), self.path)
|
||||||
|
|
||||||
return ioutil.WriteFile(self.path, data, os.ModePerm)
|
return ioutil.WriteFile(self.path, data, os.ModePerm)
|
||||||
}
|
}
|
||||||
|
|
@ -259,6 +228,7 @@ func (self *Chequebook) Stop() {
|
||||||
func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
|
func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
|
glog.V(logger.Detail).Infof("[CHEQUEBOOK] prvKey: %v", self.prvKey)
|
||||||
if amount.Cmp(common.Big0) <= 0 {
|
if amount.Cmp(common.Big0) <= 0 {
|
||||||
return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
|
return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
|
||||||
}
|
}
|
||||||
|
|
@ -273,10 +243,11 @@ func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *
|
||||||
}
|
}
|
||||||
sum := new(big.Int).Set(sent)
|
sum := new(big.Int).Set(sent)
|
||||||
sum.Add(sum, amount)
|
sum.Add(sum, amount)
|
||||||
sig, err = crypto.Sign(sigHash(self.contract, beneficiary, sum), self.prvKey)
|
|
||||||
|
sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ch = &Cheque{
|
ch = &Cheque{
|
||||||
Contract: self.contract,
|
Contract: self.contractAddr,
|
||||||
Beneficiary: beneficiary,
|
Beneficiary: beneficiary,
|
||||||
Amount: sum,
|
Amount: sum,
|
||||||
Sig: sig,
|
Sig: sig,
|
||||||
|
|
@ -301,7 +272,7 @@ func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *
|
||||||
|
|
||||||
// convenience method to cash any cheque
|
// convenience method to cash any cheque
|
||||||
func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) {
|
func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) {
|
||||||
return ch.Cash(self.owner, self.backend)
|
return ch.Cash(self.session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// data to sign: contract address, beneficiary, cumulative amount of funds ever sent
|
// data to sign: contract address, beneficiary, cumulative amount of funds ever sent
|
||||||
|
|
@ -330,13 +301,13 @@ func (self *Chequebook) Owner() common.Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend() public accessor for backend
|
// Backend() public accessor for backend
|
||||||
func (self *Chequebook) Backend() Backend {
|
func (self *Chequebook) Backend() bind.Backend {
|
||||||
return self.backend
|
return self.backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address() public accessor for contract
|
// Address() public accessor for contract
|
||||||
func (self *Chequebook) Address() common.Address {
|
func (self *Chequebook) Address() common.Address {
|
||||||
return self.contract
|
return self.contractAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deposit(amount) deposits amount to the chequebook account
|
// Deposit(amount) deposits amount to the chequebook account
|
||||||
|
|
@ -349,15 +320,19 @@ func (self *Chequebook) Deposit(amount *big.Int) (string, error) {
|
||||||
// deposit(amount) deposits amount to the chequebook account
|
// deposit(amount) deposits amount to the chequebook account
|
||||||
// caller holds the lock
|
// caller holds the lock
|
||||||
func (self *Chequebook) deposit(amount *big.Int) (string, error) {
|
func (self *Chequebook) deposit(amount *big.Int) (string, error) {
|
||||||
txhash, err := self.backend.Transact(self.owner.Hex(), self.contract.Hex(), "", amount.String(), "", "", "")
|
// since the amount is variable here, we do not use sessions
|
||||||
|
depositTransactor := bind.NewKeyedTransactor(self.prvKey)
|
||||||
|
depositTransactor.Value = amount
|
||||||
|
chbookRaw := &contract.ChequebookRaw{self.contract}
|
||||||
|
tx, err := chbookRaw.Transfer(depositTransactor)
|
||||||
// assume that transaction is actually successful, we add the amount to balance right away
|
// assume that transaction is actually successful, we add the amount to balance right away
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infof("[CHEQUEBOOK] error depositing %d wei to chequebook (%s, balance: %v, target: %v): %v", amount, self.contract.Hex(), self.balance, self.buffer, err)
|
glog.V(logger.Warn).Infof("[CHEQUEBOOK] error depositing %d wei to chequebook (%s, balance: %v, target: %v): %v", amount, self.contractAddr.Hex(), self.balance, self.buffer, err)
|
||||||
} else {
|
} else {
|
||||||
self.balance.Add(self.balance, amount)
|
self.balance.Add(self.balance, amount)
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] deposited %d wei to chequebook (%s, balance: %v, target: %v)", amount, self.contract.Hex(), self.balance, self.buffer)
|
glog.V(logger.Detail).Infof("[CHEQUEBOOK] deposited %d wei to chequebook (%s, balance: %v, target: %v)", amount, self.contractAddr.Hex(), self.balance, self.buffer)
|
||||||
}
|
}
|
||||||
return txhash, err
|
return tx.Hash().Hex(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoDeposit(interval, threshold, buffer) (re)sets interval time and amount
|
// AutoDeposit(interval, threshold, buffer) (re)sets interval time and amount
|
||||||
|
|
@ -435,43 +410,50 @@ func (self *Outbox) String() string {
|
||||||
return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.chequeBook.Balance())
|
return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.chequeBook.Balance())
|
||||||
}
|
}
|
||||||
|
|
||||||
// type ChequeQueue struct {
|
|
||||||
// beneficiary common.Address
|
|
||||||
// last map[string]*Inbox
|
|
||||||
// }
|
|
||||||
|
|
||||||
// inbox to deposit, verify and cash cheques
|
// inbox to deposit, verify and cash cheques
|
||||||
// from a single contract to single beneficiary
|
// from a single contract to single beneficiary
|
||||||
// incoming payment handler for peer to peer micropayments
|
// incoming payment handler for peer to peer micropayments
|
||||||
type Inbox struct {
|
type Inbox struct {
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
contract common.Address // peer's chequebook contract
|
contract common.Address // peer's chequebook contract
|
||||||
beneficiary common.Address // local peer's receiving address
|
beneficiary common.Address // local peer's receiving address
|
||||||
sender common.Address // local peer's address to send cashing tx from
|
sender common.Address // local peer's address to send cashing tx from
|
||||||
signer *ecdsa.PublicKey // peer's public key
|
signer *ecdsa.PublicKey // peer's public key
|
||||||
txhash string // tx hash of last cashing tx
|
txhash string // tx hash of last cashing tx
|
||||||
backend Backend // blockchain API
|
abigen bind.ContractBackend // blockchain API
|
||||||
quit chan bool // when closed causes autocash to stop
|
session *contract.ChequebookSession // abi contract backend with tx opts
|
||||||
maxUncashed *big.Int // threshold that triggers autocashing
|
quit chan bool // when closed causes autocash to stop
|
||||||
cashed *big.Int // cumulative amount cashed
|
maxUncashed *big.Int // threshold that triggers autocashing
|
||||||
cheque *Cheque // last cheque, nil if none yet received
|
cashed *big.Int // cumulative amount cashed
|
||||||
|
cheque *Cheque // last cheque, nil if none yet received
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInbox(contract, beneficiary, signer, backend) constructor for Inbox
|
// NewInbox(contract, beneficiary, signer, backend) constructor for Inbox
|
||||||
// not persisted, cumulative sum updated from blockchain when first cheque received
|
// not persisted, cumulative sum updated from blockchain when first cheque received
|
||||||
// backend used to sync amount (Call) as well as cash the cheques (Transact)
|
// backend used to sync amount (Call) as well as cash the cheques (Transact)
|
||||||
func NewInbox(contract, sender, beneficiary common.Address, signer *ecdsa.PublicKey, backend Backend) (self *Inbox, err error) {
|
func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address, signer *ecdsa.PublicKey, abigen bind.ContractBackend) (self *Inbox, err error) {
|
||||||
|
|
||||||
if signer == nil {
|
if signer == nil {
|
||||||
return nil, fmt.Errorf("signer is null")
|
return nil, fmt.Errorf("signer is null")
|
||||||
}
|
}
|
||||||
|
chbook, err := contract.NewChequebook(contractAddr, abigen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
transactOpts := bind.NewKeyedTransactor(prvKey)
|
||||||
|
transactOpts.GasLimit = gasToCash
|
||||||
|
session := &contract.ChequebookSession{
|
||||||
|
Contract: chbook,
|
||||||
|
TransactOpts: *transactOpts,
|
||||||
|
}
|
||||||
|
sender := transactOpts.From
|
||||||
|
|
||||||
self = &Inbox{
|
self = &Inbox{
|
||||||
contract: contract,
|
contract: contractAddr,
|
||||||
beneficiary: beneficiary,
|
beneficiary: beneficiary,
|
||||||
sender: sender,
|
sender: sender,
|
||||||
signer: signer,
|
signer: signer,
|
||||||
backend: backend,
|
session: session,
|
||||||
cashed: new(big.Int).Set(common.Big0),
|
cashed: new(big.Int).Set(common.Big0),
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] initialised inbox (%s -> %s) expected signer: %x", self.contract.Hex(), self.beneficiary.Hex(), crypto.FromECDSAPub(signer))
|
glog.V(logger.Detail).Infof("[CHEQUEBOOK] initialised inbox (%s -> %s) expected signer: %x", self.contract.Hex(), self.beneficiary.Hex(), crypto.FromECDSAPub(signer))
|
||||||
|
|
@ -494,7 +476,7 @@ func (self *Inbox) Stop() {
|
||||||
|
|
||||||
func (self *Inbox) Cash() (txhash string, err error) {
|
func (self *Inbox) Cash() (txhash string, err error) {
|
||||||
if self.cheque != nil {
|
if self.cheque != nil {
|
||||||
txhash, err = self.cheque.Cash(self.sender, self.backend)
|
txhash, err = self.cheque.Cash(self.session)
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] cashing cheque (total: %v) on chequebook (%s) sending to %v", self.cheque.Amount, self.contract.Hex(), self.beneficiary.Hex())
|
glog.V(logger.Detail).Infof("[CHEQUEBOOK] cashing cheque (total: %v) on chequebook (%s) sending to %v", self.cheque.Amount, self.contract.Hex(), self.beneficiary.Hex())
|
||||||
self.cashed = self.cheque.Amount
|
self.cashed = self.cheque.Amount
|
||||||
}
|
}
|
||||||
|
|
@ -548,7 +530,7 @@ func (self *Inbox) autoCash(cashInterval time.Duration) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reveive(cheque) called to deposit latest cheque to incoming Inbox
|
// Receive(cheque) called to deposit latest cheque to incoming Inbox
|
||||||
func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
||||||
ch := promise.(*Cheque)
|
ch := promise.(*Cheque)
|
||||||
|
|
||||||
|
|
@ -557,18 +539,12 @@ func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
||||||
var sum *big.Int
|
var sum *big.Int
|
||||||
if self.cheque == nil {
|
if self.cheque == nil {
|
||||||
// the sum is checked against the blockchain once a check is received
|
// the sum is checked against the blockchain once a check is received
|
||||||
tallyhex, _, err := self.backend.Call(self.beneficiary.Hex(), self.contract.Hex(), "", "", "", getSentAbiEncode(ch.Contract))
|
//
|
||||||
|
tally, err := self.session.Sent(self.beneficiary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err)
|
return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err)
|
||||||
}
|
}
|
||||||
|
sum = tally
|
||||||
tally := common.FromHex(tallyhex)
|
|
||||||
// var ok bool
|
|
||||||
// sum, ok = new(big.Int).SetString(tally, 10)
|
|
||||||
// if !ok {
|
|
||||||
// return nil, fmt.Errorf("inbox: cannot convert amount '%s' (%v) to integer", tallyhex, tally)
|
|
||||||
// }
|
|
||||||
sum = new(big.Int).SetBytes(tally)
|
|
||||||
} else {
|
} else {
|
||||||
sum = self.cheque.Amount
|
sum = self.cheque.Amount
|
||||||
}
|
}
|
||||||
|
|
@ -590,37 +566,6 @@ func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
||||||
return amount, err
|
return amount, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RSV representation of signature
|
|
||||||
func sig2rsv(sig []byte) (v byte, r, s []byte) {
|
|
||||||
v = sig[64] + 27
|
|
||||||
r = sig[:32]
|
|
||||||
s = sig[32:64]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSentAbiEncode(beneficiary common.Address) string {
|
|
||||||
var beneficiary32 [32]byte
|
|
||||||
copy(beneficiary32[12:], beneficiary.Bytes())
|
|
||||||
return getSentAbiPre + common.Bytes2Hex(beneficiary32[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// abi encoding of a cheque to send as eth tx data
|
|
||||||
func (self *Cheque) cashAbiEncode() string {
|
|
||||||
v, r, s := sig2rsv(self.Sig)
|
|
||||||
// cashAbiPre, beneficiary, amount, v, r, s
|
|
||||||
bigamount := self.Amount.Bytes()
|
|
||||||
if len(bigamount) > 32 {
|
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] number too big: %v (>32 bytes)", self.Amount)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var beneficiary32, amount32, vabi [32]byte
|
|
||||||
copy(beneficiary32[12:], self.Beneficiary.Bytes())
|
|
||||||
copy(amount32[32-len(bigamount):32], bigamount)
|
|
||||||
vabi[31] = v
|
|
||||||
return cashAbiPre + common.Bytes2Hex(beneficiary32[:]) + common.Bytes2Hex(amount32[:]) +
|
|
||||||
common.Bytes2Hex(vabi[:]) + common.Bytes2Hex(r) + common.Bytes2Hex(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify(cheque) verifies cheque for signer, contract, beneficiary, amount, valid signature
|
// Verify(cheque) verifies cheque for signer, contract, beneficiary, amount, valid signature
|
||||||
func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
|
func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
|
||||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] verify cheque: %v - sum: %v", self, sum)
|
glog.V(logger.Detail).Infof("[CHEQUEBOOK] verify cheque: %v - sum: %v", self, sum)
|
||||||
|
|
@ -653,8 +598,21 @@ func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary com
|
||||||
return amount, nil
|
return amount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cash(backend) will cash the check using xeth backend to send a transaction
|
// v/r/s representation of signature
|
||||||
// Beneficiary address should be unlocked
|
func sig2vrs(sig []byte) (v *big.Int, r, s [32]byte) {
|
||||||
func (self *Cheque) Cash(sender common.Address, backend Backend) (string, error) {
|
v = big.NewInt(int64(sig[64] + 27))
|
||||||
return backend.Transact(sender.Hex(), self.Contract.Hex(), "", "", "", gasToCash, self.cashAbiEncode())
|
copy(r[:], sig[:32])
|
||||||
|
copy(s[:], sig[32:64])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cash(backend) will cash the check using abi contract backend to send a transaction
|
||||||
|
// Beneficiary address should be unlocked
|
||||||
|
func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) {
|
||||||
|
v, r, s := sig2vrs(self.Sig)
|
||||||
|
tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return tx.Hash().Hex(), nil
|
||||||
}
|
}
|
||||||
|
|
@ -1,88 +1,103 @@
|
||||||
package chequebook
|
package chequebook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/chequebook/contract"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
key0, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
key1, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||||
|
key2, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||||
|
addr0 = crypto.PubkeyToAddress(key0.PublicKey)
|
||||||
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||||
)
|
)
|
||||||
|
|
||||||
type testBackend struct {
|
type testBackend struct {
|
||||||
calls []string
|
*backends.SimulatedBackend
|
||||||
errs []error
|
}
|
||||||
txs []string
|
|
||||||
|
func accounts() []core.GenesisAccount {
|
||||||
|
|
||||||
|
return []core.GenesisAccount{
|
||||||
|
core.GenesisAccount{addr0, big.NewInt(1000000000)},
|
||||||
|
core.GenesisAccount{addr1, big.NewInt(1000000000)},
|
||||||
|
core.GenesisAccount{addr2, big.NewInt(1000000000)},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestBackend() *testBackend {
|
func newTestBackend() *testBackend {
|
||||||
return &testBackend{}
|
accs := accounts()
|
||||||
}
|
return &testBackend{SimulatedBackend: backends.NewSimulatedBackend(accs...)}
|
||||||
|
|
||||||
func (b *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
|
||||||
txhash := string(crypto.Sha3([]byte(codeStr)))
|
|
||||||
b.txs = append(b.txs, txhash)
|
|
||||||
return txhash, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) {
|
|
||||||
if len(b.calls) == 0 {
|
|
||||||
panic("test backend called too many times")
|
|
||||||
}
|
|
||||||
res := b.calls[0]
|
|
||||||
err := b.errs[0]
|
|
||||||
b.calls = b.calls[1:]
|
|
||||||
b.errs = b.errs[1:]
|
|
||||||
return res, "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) GetTxReceipt(txhash common.Hash) *types.Receipt {
|
func (b *testBackend) GetTxReceipt(txhash common.Hash) *types.Receipt {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) CodeAt(address string) string {
|
func (b *testBackend) CodeAt(address common.Address) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func genAddr() common.Address {
|
func (b *testBackend) BalanceAt(address common.Address) *big.Int {
|
||||||
prvKey, _ := crypto.GenerateKey()
|
return big.NewInt(0)
|
||||||
return crypto.PubkeyToAddress(prvKey.PublicKey)
|
}
|
||||||
|
|
||||||
|
func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) {
|
||||||
|
deployTransactor := bind.NewKeyedTransactor(prvKey)
|
||||||
|
deployTransactor.Value = amount
|
||||||
|
addr, _, _, err := contract.DeployChequebook(deployTransactor, backend)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
|
backend.Commit()
|
||||||
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIssueAndReceive(t *testing.T) {
|
func TestIssueAndReceive(t *testing.T) {
|
||||||
prvKey, _ := crypto.GenerateKey()
|
|
||||||
sender := genAddr()
|
|
||||||
path := "/tmp/checkbook.json"
|
path := "/tmp/checkbook.json"
|
||||||
chbook, err := NewChequebook(path, sender, prvKey, nil)
|
backend := newTestBackend()
|
||||||
|
addr0, err := deploy(key0, big.NewInt(0), backend.SimulatedBackend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expected no error, got %v", err)
|
t.Fatalf("deploy contract: expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
recipient := genAddr()
|
chbook, err := NewChequebook(path, addr0, key0, backend)
|
||||||
chbook.sent[recipient] = new(big.Int).SetUint64(42)
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
chbook.sent[addr1] = new(big.Int).SetUint64(42)
|
||||||
amount := common.Big1
|
amount := common.Big1
|
||||||
ch, err := chbook.Issue(recipient, amount)
|
ch, err := chbook.Issue(addr1, amount)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected insufficient funds error, got none")
|
t.Fatalf("expected insufficient funds error, got none")
|
||||||
}
|
}
|
||||||
|
|
||||||
chbook.balance = new(big.Int).Set(common.Big1)
|
chbook.balance = new(big.Int).Set(common.Big1)
|
||||||
if chbook.Balance().Cmp(common.Big1) != 0 {
|
if chbook.Balance().Cmp(common.Big1) != 0 {
|
||||||
t.Errorf("expected: %v, got %v", "0", chbook.Balance())
|
t.Fatalf("expected: %v, got %v", "0", chbook.Balance())
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, err = chbook.Issue(recipient, amount)
|
ch, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if chbook.Balance().Cmp(common.Big0) != 0 {
|
if chbook.Balance().Cmp(common.Big0) != 0 {
|
||||||
t.Errorf("expected: %v, got %v", "0", chbook.Balance())
|
t.Errorf("expected: %v, got %v", "0", chbook.Balance())
|
||||||
}
|
}
|
||||||
|
|
||||||
backend := newTestBackend()
|
chbox, err := NewInbox(key1, addr0, addr1, &key0.PublicKey, backend)
|
||||||
backend.calls = []string{common.ToHex(big.NewInt(42).Bytes())}
|
|
||||||
backend.errs = []error{nil}
|
|
||||||
chbox, err := NewInbox(sender, recipient, recipient, &prvKey.PublicKey, backend)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -92,27 +107,25 @@ func TestIssueAndReceive(t *testing.T) {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if received.Cmp(common.Big1) != 0 {
|
if received.Cmp(big.NewInt(43)) != 0 {
|
||||||
t.Errorf("expected: %v, got %v", "1", received)
|
t.Errorf("expected: %v, got %v", "43", received)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckbookFile(t *testing.T) {
|
func TestCheckbookFile(t *testing.T) {
|
||||||
prvKey, _ := crypto.GenerateKey()
|
|
||||||
sender := genAddr()
|
|
||||||
path := "/tmp/checkbook.json"
|
path := "/tmp/checkbook.json"
|
||||||
chbook, err := NewChequebook(path, sender, prvKey, nil)
|
backend := newTestBackend()
|
||||||
|
chbook, err := NewChequebook(path, addr0, key0, backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
recipient := genAddr()
|
chbook.sent[addr1] = new(big.Int).SetUint64(42)
|
||||||
chbook.sent[recipient] = new(big.Int).SetUint64(42)
|
|
||||||
chbook.balance = new(big.Int).Set(common.Big1)
|
chbook.balance = new(big.Int).Set(common.Big1)
|
||||||
|
|
||||||
chbook.Save()
|
chbook.Save()
|
||||||
|
|
||||||
chbook, err = LoadChequebook(path, prvKey, nil)
|
chbook, err = LoadChequebook(path, key0, backend, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +133,7 @@ func TestCheckbookFile(t *testing.T) {
|
||||||
t.Errorf("expected: %v, got %v", "0", chbook.Balance())
|
t.Errorf("expected: %v, got %v", "0", chbook.Balance())
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, err := chbook.Issue(recipient, common.Big1)
|
ch, err := chbook.Issue(addr1, common.Big1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -135,31 +148,35 @@ func TestCheckbookFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerifyErrors(t *testing.T) {
|
func TestVerifyErrors(t *testing.T) {
|
||||||
prvKey, _ := crypto.GenerateKey()
|
|
||||||
sender0 := genAddr()
|
|
||||||
sender1 := genAddr()
|
|
||||||
path0 := "/tmp/checkbook0.json"
|
path0 := "/tmp/checkbook0.json"
|
||||||
chbook0, err := NewChequebook(path0, sender0, prvKey, nil)
|
backend := newTestBackend()
|
||||||
|
contr0, err := deploy(key0, common.Big2, backend.SimulatedBackend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expected no error, got %v", err)
|
t.Errorf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
chbook0, err := NewChequebook(path0, contr0, key0, backend)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
path1 := "/tmp/checkbook1.json"
|
path1 := "/tmp/checkbook1.json"
|
||||||
chbook1, err := NewChequebook(path1, sender1, prvKey, nil)
|
contr1, err := deploy(key1, common.Big2, backend.SimulatedBackend)
|
||||||
|
chbook1, err := NewChequebook(path1, contr1, key1, backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expected no error, got %v", err)
|
t.Errorf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
recipient0 := genAddr()
|
|
||||||
recipient1 := genAddr()
|
chbook0.sent[addr1] = new(big.Int).SetUint64(42)
|
||||||
chbook0.balance = new(big.Int).Set(common.Big2)
|
chbook0.balance = new(big.Int).Set(common.Big2)
|
||||||
chbook1.balance = new(big.Int).Set(common.Big1)
|
chbook1.balance = new(big.Int).Set(common.Big1)
|
||||||
chbook0.sent[recipient0] = new(big.Int).SetUint64(42)
|
|
||||||
amount := common.Big1
|
amount := common.Big1
|
||||||
ch0, err := chbook0.Issue(recipient0, amount)
|
ch0, err := chbook0.Issue(addr1, amount)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
backend := newTestBackend()
|
time.Sleep(5)
|
||||||
backend.calls = []string{common.ToHex(big.NewInt(42).Bytes())}
|
chbox, err := NewInbox(key1, contr0, addr1, &key0.PublicKey, backend)
|
||||||
backend.errs = []error{nil}
|
|
||||||
chbox, err := NewInbox(sender0, recipient0, recipient0, &prvKey.PublicKey, backend)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -169,39 +186,39 @@ func TestVerifyErrors(t *testing.T) {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if received.Cmp(common.Big1) != 0 {
|
if received.Cmp(big.NewInt(43)) != 0 {
|
||||||
t.Errorf("expected: %v, got %v", "1", received)
|
t.Errorf("expected: %v, got %v", "43", received)
|
||||||
}
|
}
|
||||||
|
|
||||||
ch1, err := chbook0.Issue(recipient1, amount)
|
ch1, err := chbook0.Issue(addr2, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
received, err = chbox.Receive(ch1)
|
received, err = chbox.Receive(ch1)
|
||||||
t.Log(err)
|
t.Logf("correct error: %v", err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected receiver error, got none")
|
t.Fatalf("expected receiver error, got none")
|
||||||
}
|
}
|
||||||
|
|
||||||
ch2, err := chbook1.Issue(recipient0, amount)
|
ch2, err := chbook1.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
received, err = chbox.Receive(ch2)
|
received, err = chbox.Receive(ch2)
|
||||||
t.Log(err)
|
t.Logf("correct error: %v", err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected sender error, got none")
|
t.Fatalf("expected sender error, got none")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = chbook1.Issue(recipient0, new(big.Int).SetInt64(-1))
|
_, err = chbook1.Issue(addr1, new(big.Int).SetInt64(-1))
|
||||||
t.Log(err)
|
t.Logf("correct error: %v", err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected incorrect amount error, got none")
|
t.Fatalf("expected incorrect amount error, got none")
|
||||||
}
|
}
|
||||||
|
|
||||||
received, err = chbox.Receive(ch0)
|
received, err = chbox.Receive(ch0)
|
||||||
t.Log(err)
|
t.Logf("correct error: %v", err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected incorrect amount error, got none")
|
t.Fatalf("expected incorrect amount error, got none")
|
||||||
}
|
}
|
||||||
|
|
@ -209,31 +226,27 @@ func TestVerifyErrors(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeposit(t *testing.T) {
|
func TestDeposit(t *testing.T) {
|
||||||
prvKey, _ := crypto.GenerateKey()
|
path0 := "/tmp/checkbook0.json"
|
||||||
sender := genAddr()
|
|
||||||
path := "/tmp/checkbook.json"
|
|
||||||
|
|
||||||
backend := newTestBackend()
|
backend := newTestBackend()
|
||||||
chbook, err := NewChequebook(path, sender, prvKey, backend)
|
contr0, err := deploy(key0, common.Big2, backend.SimulatedBackend)
|
||||||
|
chbook, err := NewChequebook(path0, contr0, key0, backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expected no error, got %v", err)
|
t.Errorf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
balance := new(big.Int).SetUint64(42)
|
balance := new(big.Int).SetUint64(42)
|
||||||
chbook.Deposit(balance)
|
chbook.Deposit(balance)
|
||||||
if len(backend.txs) != 1 {
|
backend.Commit()
|
||||||
t.Fatalf("expected 1 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
|
||||||
if chbook.balance.Cmp(balance) != 0 {
|
if chbook.balance.Cmp(balance) != 0 {
|
||||||
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
recipient := genAddr()
|
|
||||||
amount := common.Big1
|
amount := common.Big1
|
||||||
_, err = chbook.Issue(recipient, amount)
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
exp := new(big.Int).SetUint64(41)
|
exp := new(big.Int).SetUint64(41)
|
||||||
if chbook.balance.Cmp(exp) != 0 {
|
if chbook.balance.Cmp(exp) != 0 {
|
||||||
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
||||||
|
|
@ -241,36 +254,33 @@ func TestDeposit(t *testing.T) {
|
||||||
|
|
||||||
// autodeposit on each issue
|
// autodeposit on each issue
|
||||||
chbook.AutoDeposit(0, balance, balance)
|
chbook.AutoDeposit(0, balance, balance)
|
||||||
_, err = chbook.Issue(recipient, amount)
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
_, err = chbook.Issue(recipient, amount)
|
backend.Commit()
|
||||||
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
if len(backend.txs) != 3 {
|
|
||||||
t.Fatalf("expected 3 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
|
||||||
if chbook.balance.Cmp(balance) != 0 {
|
if chbook.balance.Cmp(balance) != 0 {
|
||||||
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// autodeposit off
|
// autodeposit off
|
||||||
chbook.AutoDeposit(0, common.Big0, balance)
|
chbook.AutoDeposit(0, common.Big0, balance)
|
||||||
_, err = chbook.Issue(recipient, amount)
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
_, err = chbook.Issue(recipient, amount)
|
backend.Commit()
|
||||||
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
|
|
||||||
if len(backend.txs) != 3 {
|
|
||||||
t.Fatalf("expected 3 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
|
||||||
exp = new(big.Int).SetUint64(40)
|
exp = new(big.Int).SetUint64(40)
|
||||||
if chbook.balance.Cmp(exp) != 0 {
|
if chbook.balance.Cmp(exp) != 0 {
|
||||||
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
||||||
|
|
@ -279,59 +289,52 @@ func TestDeposit(t *testing.T) {
|
||||||
// autodeposit every 10ms if new cheque issued
|
// autodeposit every 10ms if new cheque issued
|
||||||
interval := 30 * time.Millisecond
|
interval := 30 * time.Millisecond
|
||||||
chbook.AutoDeposit(interval, common.Big1, balance)
|
chbook.AutoDeposit(interval, common.Big1, balance)
|
||||||
_, err = chbook.Issue(recipient, amount)
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
_, err = chbook.Issue(recipient, amount)
|
backend.Commit()
|
||||||
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
|
|
||||||
if len(backend.txs) != 3 {
|
|
||||||
t.Fatalf("expected 3 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
|
||||||
exp = new(big.Int).SetUint64(38)
|
exp = new(big.Int).SetUint64(38)
|
||||||
if chbook.balance.Cmp(exp) != 0 {
|
if chbook.balance.Cmp(exp) != 0 {
|
||||||
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(3 * interval)
|
time.Sleep(3 * interval)
|
||||||
if len(backend.txs) != 4 {
|
backend.Commit()
|
||||||
t.Fatalf("expected 4 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
|
||||||
if chbook.balance.Cmp(balance) != 0 {
|
if chbook.balance.Cmp(balance) != 0 {
|
||||||
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
exp = new(big.Int).SetUint64(40)
|
exp = new(big.Int).SetUint64(40)
|
||||||
chbook.AutoDeposit(4*interval, exp, balance)
|
chbook.AutoDeposit(4*interval, exp, balance)
|
||||||
_, err = chbook.Issue(recipient, amount)
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
_, err = chbook.Issue(recipient, amount)
|
backend.Commit()
|
||||||
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(3 * interval)
|
time.Sleep(3 * interval)
|
||||||
if len(backend.txs) != 4 {
|
backend.Commit()
|
||||||
t.Fatalf("expected 4 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
|
||||||
if chbook.balance.Cmp(exp) != 0 {
|
if chbook.balance.Cmp(exp) != 0 {
|
||||||
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = chbook.Issue(recipient, amount)
|
_, err = chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
time.Sleep(1 * interval)
|
time.Sleep(1 * interval)
|
||||||
|
backend.Commit()
|
||||||
|
|
||||||
if len(backend.txs) != 5 {
|
|
||||||
t.Fatalf("expected 5 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
|
||||||
if chbook.balance.Cmp(balance) != 0 {
|
if chbook.balance.Cmp(balance) != 0 {
|
||||||
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
||||||
}
|
}
|
||||||
|
|
@ -339,21 +342,20 @@ func TestDeposit(t *testing.T) {
|
||||||
chbook.AutoDeposit(1*interval, common.Big0, balance)
|
chbook.AutoDeposit(1*interval, common.Big0, balance)
|
||||||
chbook.Stop()
|
chbook.Stop()
|
||||||
|
|
||||||
_, err = chbook.Issue(recipient, common.Big1)
|
_, err = chbook.Issue(addr1, common.Big1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
|
|
||||||
_, err = chbook.Issue(recipient, common.Big2)
|
_, err = chbook.Issue(addr1, common.Big2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(1 * interval)
|
time.Sleep(1 * interval)
|
||||||
|
backend.Commit()
|
||||||
|
|
||||||
if len(backend.txs) != 5 {
|
|
||||||
t.Fatalf("expected 5 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
|
||||||
exp = new(big.Int).SetUint64(39)
|
exp = new(big.Int).SetUint64(39)
|
||||||
if chbook.balance.Cmp(exp) != 0 {
|
if chbook.balance.Cmp(exp) != 0 {
|
||||||
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
||||||
|
|
@ -362,26 +364,22 @@ func TestDeposit(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCash(t *testing.T) {
|
func TestCash(t *testing.T) {
|
||||||
prvKey, _ := crypto.GenerateKey()
|
|
||||||
sender := genAddr()
|
|
||||||
path := "/tmp/checkbook.json"
|
path := "/tmp/checkbook.json"
|
||||||
chbook, err := NewChequebook(path, sender, prvKey, nil)
|
backend := newTestBackend()
|
||||||
|
contr0, err := deploy(key0, common.Big2, backend.SimulatedBackend)
|
||||||
|
chbook, err := NewChequebook(path, contr0, key0, backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expected no error, got %v", err)
|
t.Errorf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
recipient := genAddr()
|
chbook.sent[addr1] = new(big.Int).SetUint64(42)
|
||||||
chbook.sent[recipient] = new(big.Int).SetUint64(42)
|
|
||||||
amount := common.Big1
|
amount := common.Big1
|
||||||
chbook.balance = new(big.Int).Set(common.Big1)
|
chbook.balance = new(big.Int).Set(common.Big1)
|
||||||
ch, err := chbook.Issue(recipient, amount)
|
ch, err := chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
backend := newTestBackend()
|
chbox, err := NewInbox(key1, contr0, addr1, &key0.PublicKey, backend)
|
||||||
backend.calls = []string{common.ToHex(big.NewInt(42).Bytes())}
|
|
||||||
backend.errs = []error{nil}
|
|
||||||
chbox, err := NewInbox(sender, recipient, recipient, &prvKey.PublicKey, backend)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -391,20 +389,20 @@ func TestCash(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
_, err = ch.Cash(recipient, backend)
|
_, err = ch.Cash(chbook.session)
|
||||||
if len(backend.txs) != 1 {
|
backend.Commit()
|
||||||
t.Fatalf("expected 1 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
|
||||||
|
|
||||||
chbook.balance = new(big.Int).Set(common.Big3)
|
chbook.balance = new(big.Int).Set(common.Big3)
|
||||||
ch0, err := chbook.Issue(recipient, amount)
|
ch0, err := chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
ch1, err := chbook.Issue(recipient, amount)
|
backend.Commit()
|
||||||
|
ch1, err := chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
|
|
||||||
interval := 10 * time.Millisecond
|
interval := 10 * time.Millisecond
|
||||||
// setting autocash with interval of 10ms
|
// setting autocash with interval of 10ms
|
||||||
|
|
@ -417,82 +415,119 @@ func TestCash(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
// after < interval time and 2 cheques received, no new cashing tx is sent
|
backend.Commit()
|
||||||
if len(backend.txs) != 1 {
|
// expBalance := big.NewInt(2)
|
||||||
t.Fatalf("expected 1 txs to send, got %v", len(backend.txs))
|
// gotBalance := backend.BalanceAt(addr1)
|
||||||
}
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
// after 3x interval time and 2 cheques received, exactly one cashing tx is sent
|
// after 3x interval time and 2 cheques received, exactly one cashing tx is sent
|
||||||
time.Sleep(4 * interval)
|
time.Sleep(4 * interval)
|
||||||
if len(backend.txs) != 2 {
|
backend.Commit()
|
||||||
t.Fatalf("expected 2 txs to send, got %v", len(backend.txs))
|
|
||||||
}
|
// expBalance = big.NewInt(4)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
// after stopping autocash no more tx are sent
|
// after stopping autocash no more tx are sent
|
||||||
ch2, err := chbook.Issue(recipient, amount)
|
ch2, err := chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
chbox.Stop()
|
chbox.Stop()
|
||||||
time.Sleep(interval) // make sure loop stops
|
|
||||||
_, err = chbox.Receive(ch2)
|
_, err = chbox.Receive(ch2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
time.Sleep(2 * interval)
|
time.Sleep(2 * interval)
|
||||||
if len(backend.txs) != 2 {
|
backend.Commit()
|
||||||
t.Fatalf("expected 2 txs to send, got %v", len(backend.txs))
|
// expBalance = big.NewInt(4)
|
||||||
}
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
// autocash below 1
|
// autocash below 1
|
||||||
chbook.balance = new(big.Int).Set(common.Big2)
|
chbook.balance = big.NewInt(2)
|
||||||
chbox.AutoCash(0, common.Big1)
|
chbox.AutoCash(0, common.Big1)
|
||||||
|
|
||||||
ch3, err := chbook.Issue(recipient, amount)
|
ch3, err := chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
ch4, err := chbook.Issue(recipient, amount)
|
backend.Commit()
|
||||||
|
// expBalance = big.NewInt(4)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
|
ch4, err := chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
|
|
||||||
_, err = chbox.Receive(ch3)
|
_, err = chbox.Receive(ch3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
_, err = chbox.Receive(ch4)
|
_, err = chbox.Receive(ch4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
|
|
||||||
// 2 checks of amount 1 received, exactly 1 tx is sent
|
// 2 checks of amount 1 received, exactly 1 tx is sent
|
||||||
if len(backend.txs) != 3 {
|
// expBalance = big.NewInt(6)
|
||||||
t.Fatalf("expected 3 txs to send, got %v", len(backend.txs))
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
}
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
// autochash on receipt when maxUncashed is 0
|
// autochash on receipt when maxUncashed is 0
|
||||||
chbook.balance = new(big.Int).Set(common.Big2)
|
chbook.balance = new(big.Int).Set(common.Big2)
|
||||||
chbox.AutoCash(0, common.Big0)
|
chbox.AutoCash(0, common.Big0)
|
||||||
|
|
||||||
ch5, err := chbook.Issue(recipient, amount)
|
ch5, err := chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
ch6, err := chbook.Issue(recipient, amount)
|
backend.Commit()
|
||||||
|
// expBalance = big.NewInt(5)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
|
ch6, err := chbook.Issue(addr1, amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = chbox.Receive(ch5)
|
_, err = chbox.Receive(ch5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
|
// expBalance = big.NewInt(4)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
_, err = chbox.Receive(ch6)
|
_, err = chbox.Receive(ch6)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
backend.Commit()
|
||||||
if len(backend.txs) != 5 {
|
// expBalance = big.NewInt(6)
|
||||||
t.Fatalf("expected 5 txs to send, got %v", len(backend.txs))
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
}
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
542
swarm/services/chequebook/contract/chequebook.go
Normal file
542
swarm/services/chequebook/contract/chequebook.go
Normal file
|
|
@ -0,0 +1,542 @@
|
||||||
|
// This file is an automatically generated Go binding. Do not modify as any
|
||||||
|
// change will likely be lost upon the next re-generation!
|
||||||
|
|
||||||
|
package contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChequebookABI is the input ABI used to generate the binding from.
|
||||||
|
const ChequebookABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"sent","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"amount","type":"uint256"},{"name":"sig_v","type":"uint8"},{"name":"sig_r","type":"bytes32"},{"name":"sig_s","type":"bytes32"}],"name":"cash","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"deadbeat","type":"address"}],"name":"Overdraft","type":"event"}]`
|
||||||
|
|
||||||
|
// ChequebookBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const ChequebookBin = `0x606060405260008054600160a060020a031916331790556101fe806100246000396000f3606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156101fc57600054600160a060020a0316ff5b6100ab60043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100b5575b505050505050565b6060908152602090f35b30600160a060020a039081166c0100000000000000000000000090810260609081529188160260745260888690526048812080825260ff8616608090815260a086905260c0859052909260019260e0926020928290866161da5a03f11561000257505060405151600054600160a060020a03908116911614610136576100a3565b85600160a060020a031660006001600050600089600160a060020a03168152602001908152602001600020600050548703604051809050600060405180830381858888f19350505050156101b057846001600050600088600160a060020a03168152602001908152602001600020600050819055506100a3565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff5b56`
|
||||||
|
|
||||||
|
// DeployChequebook deploys a new Ethereum contract, binding an instance of Chequebook to it.
|
||||||
|
func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Chequebook, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(ChequebookABI))
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ChequebookBin), backend)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
return address, tx, &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chequebook is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type Chequebook struct {
|
||||||
|
ChequebookCaller // Read-only binding to the contract
|
||||||
|
ChequebookTransactor // Write-only binding to the contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChequebookCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type ChequebookCaller struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChequebookTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type ChequebookTransactor struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChequebookSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type ChequebookSession struct {
|
||||||
|
Contract *Chequebook // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChequebookCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type ChequebookCallerSession struct {
|
||||||
|
Contract *ChequebookCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChequebookTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type ChequebookTransactorSession struct {
|
||||||
|
Contract *ChequebookTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChequebookRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type ChequebookRaw struct {
|
||||||
|
Contract *Chequebook // Generic contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChequebookCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type ChequebookCallerRaw struct {
|
||||||
|
Contract *ChequebookCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChequebookTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type ChequebookTransactorRaw struct {
|
||||||
|
Contract *ChequebookTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChequebook creates a new instance of Chequebook, bound to a specific deployed contract.
|
||||||
|
func NewChequebook(address common.Address, backend bind.ContractBackend) (*Chequebook, error) {
|
||||||
|
contract, err := bindChequebook(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChequebookCaller creates a new read-only instance of Chequebook, bound to a specific deployed contract.
|
||||||
|
func NewChequebookCaller(address common.Address, caller bind.ContractCaller) (*ChequebookCaller, error) {
|
||||||
|
contract, err := bindChequebook(address, caller, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ChequebookCaller{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChequebookTransactor creates a new write-only instance of Chequebook, bound to a specific deployed contract.
|
||||||
|
func NewChequebookTransactor(address common.Address, transactor bind.ContractTransactor) (*ChequebookTransactor, error) {
|
||||||
|
contract, err := bindChequebook(address, nil, transactor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ChequebookTransactor{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindChequebook binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindChequebook(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(ChequebookABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Chequebook *ChequebookRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Chequebook.Contract.ChequebookCaller.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Chequebook *ChequebookRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Chequebook.Contract.ChequebookTransactor.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Chequebook *ChequebookRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Chequebook.Contract.ChequebookTransactor.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Chequebook *ChequebookCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Chequebook.Contract.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Chequebook *ChequebookTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Chequebook.Contract.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Chequebook *ChequebookTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Chequebook.Contract.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent is a free data retrieval call binding the contract method 0x7bf786f8.
|
||||||
|
//
|
||||||
|
// Solidity: function sent( address) constant returns(uint256)
|
||||||
|
func (_Chequebook *ChequebookCaller) Sent(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) {
|
||||||
|
var (
|
||||||
|
ret0 = new(*big.Int)
|
||||||
|
)
|
||||||
|
out := ret0
|
||||||
|
err := _Chequebook.contract.Call(opts, out, "sent", arg0)
|
||||||
|
return *ret0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent is a free data retrieval call binding the contract method 0x7bf786f8.
|
||||||
|
//
|
||||||
|
// Solidity: function sent( address) constant returns(uint256)
|
||||||
|
func (_Chequebook *ChequebookSession) Sent(arg0 common.Address) (*big.Int, error) {
|
||||||
|
return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent is a free data retrieval call binding the contract method 0x7bf786f8.
|
||||||
|
//
|
||||||
|
// Solidity: function sent( address) constant returns(uint256)
|
||||||
|
func (_Chequebook *ChequebookCallerSession) Sent(arg0 common.Address) (*big.Int, error) {
|
||||||
|
return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cash is a paid mutator transaction binding the contract method 0xfbf788d6.
|
||||||
|
//
|
||||||
|
// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns()
|
||||||
|
func (_Chequebook *ChequebookTransactor) Cash(opts *bind.TransactOpts, beneficiary common.Address, amount *big.Int, sig_v *big.Int, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) {
|
||||||
|
return _Chequebook.contract.Transact(opts, "cash", beneficiary, amount, sig_v, sig_r, sig_s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cash is a paid mutator transaction binding the contract method 0xfbf788d6.
|
||||||
|
//
|
||||||
|
// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns()
|
||||||
|
func (_Chequebook *ChequebookSession) Cash(beneficiary common.Address, amount *big.Int, sig_v *big.Int, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) {
|
||||||
|
return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cash is a paid mutator transaction binding the contract method 0xfbf788d6.
|
||||||
|
//
|
||||||
|
// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns()
|
||||||
|
func (_Chequebook *ChequebookTransactorSession) Cash(beneficiary common.Address, amount *big.Int, sig_v *big.Int, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) {
|
||||||
|
return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Chequebook *ChequebookTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Chequebook.contract.Transact(opts, "kill")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Chequebook *ChequebookSession) Kill() (*types.Transaction, error) {
|
||||||
|
return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Chequebook *ChequebookTransactorSession) Kill() (*types.Transaction, error) {
|
||||||
|
return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalABI is the input ABI used to generate the binding from.
|
||||||
|
const MortalABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"}]`
|
||||||
|
|
||||||
|
// MortalBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const MortalBin = `0x606060405260008054600160a060020a03191633179055605c8060226000396000f3606060405260e060020a600035046341c0e1b58114601a575b005b60186000543373ffffffffffffffffffffffffffffffffffffffff90811691161415605a5760005473ffffffffffffffffffffffffffffffffffffffff16ff5b56`
|
||||||
|
|
||||||
|
// DeployMortal deploys a new Ethereum contract, binding an instance of Mortal to it.
|
||||||
|
func DeployMortal(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Mortal, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(MortalABI))
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(MortalBin), backend)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
return address, tx, &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mortal is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type Mortal struct {
|
||||||
|
MortalCaller // Read-only binding to the contract
|
||||||
|
MortalTransactor // Write-only binding to the contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type MortalCaller struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type MortalTransactor struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type MortalSession struct {
|
||||||
|
Contract *Mortal // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type MortalCallerSession struct {
|
||||||
|
Contract *MortalCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type MortalTransactorSession struct {
|
||||||
|
Contract *MortalTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type MortalRaw struct {
|
||||||
|
Contract *Mortal // Generic contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type MortalCallerRaw struct {
|
||||||
|
Contract *MortalCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type MortalTransactorRaw struct {
|
||||||
|
Contract *MortalTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMortal creates a new instance of Mortal, bound to a specific deployed contract.
|
||||||
|
func NewMortal(address common.Address, backend bind.ContractBackend) (*Mortal, error) {
|
||||||
|
contract, err := bindMortal(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMortalCaller creates a new read-only instance of Mortal, bound to a specific deployed contract.
|
||||||
|
func NewMortalCaller(address common.Address, caller bind.ContractCaller) (*MortalCaller, error) {
|
||||||
|
contract, err := bindMortal(address, caller, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &MortalCaller{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMortalTransactor creates a new write-only instance of Mortal, bound to a specific deployed contract.
|
||||||
|
func NewMortalTransactor(address common.Address, transactor bind.ContractTransactor) (*MortalTransactor, error) {
|
||||||
|
contract, err := bindMortal(address, nil, transactor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &MortalTransactor{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindMortal binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindMortal(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(MortalABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Mortal *MortalRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Mortal.Contract.MortalCaller.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Mortal *MortalRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.MortalTransactor.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Mortal *MortalRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.MortalTransactor.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Mortal *MortalCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Mortal.Contract.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Mortal *MortalTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Mortal *MortalTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Mortal *MortalTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Mortal.contract.Transact(opts, "kill")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Mortal *MortalSession) Kill() (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.Kill(&_Mortal.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Mortal *MortalTransactorSession) Kill() (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.Kill(&_Mortal.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedABI is the input ABI used to generate the binding from.
|
||||||
|
const OwnedABI = `[{"inputs":[],"type":"constructor"}]`
|
||||||
|
|
||||||
|
// OwnedBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const OwnedBin = `0x606060405260008054600160a060020a0319163317905560068060226000396000f3606060405200`
|
||||||
|
|
||||||
|
// DeployOwned deploys a new Ethereum contract, binding an instance of Owned to it.
|
||||||
|
func DeployOwned(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Owned, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(OwnedABI))
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(OwnedBin), backend)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
return address, tx, &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owned is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type Owned struct {
|
||||||
|
OwnedCaller // Read-only binding to the contract
|
||||||
|
OwnedTransactor // Write-only binding to the contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedCaller struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedTransactor struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type OwnedSession struct {
|
||||||
|
Contract *Owned // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type OwnedCallerSession struct {
|
||||||
|
Contract *OwnedCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type OwnedTransactorSession struct {
|
||||||
|
Contract *OwnedTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type OwnedRaw struct {
|
||||||
|
Contract *Owned // Generic contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedCallerRaw struct {
|
||||||
|
Contract *OwnedCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedTransactorRaw struct {
|
||||||
|
Contract *OwnedTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOwned creates a new instance of Owned, bound to a specific deployed contract.
|
||||||
|
func NewOwned(address common.Address, backend bind.ContractBackend) (*Owned, error) {
|
||||||
|
contract, err := bindOwned(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOwnedCaller creates a new read-only instance of Owned, bound to a specific deployed contract.
|
||||||
|
func NewOwnedCaller(address common.Address, caller bind.ContractCaller) (*OwnedCaller, error) {
|
||||||
|
contract, err := bindOwned(address, caller, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &OwnedCaller{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOwnedTransactor creates a new write-only instance of Owned, bound to a specific deployed contract.
|
||||||
|
func NewOwnedTransactor(address common.Address, transactor bind.ContractTransactor) (*OwnedTransactor, error) {
|
||||||
|
contract, err := bindOwned(address, nil, transactor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &OwnedTransactor{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindOwned binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindOwned(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(OwnedABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Owned *OwnedRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Owned.Contract.OwnedCaller.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Owned *OwnedRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Owned.Contract.OwnedTransactor.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Owned *OwnedRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Owned.Contract.OwnedTransactor.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Owned *OwnedCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Owned.Contract.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Owned *OwnedTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Owned.Contract.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Owned *OwnedTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Owned.Contract.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -4,19 +4,11 @@ import "mortal";
|
||||||
/// @author Daniel A. Nagy <daniel@ethdev.com>
|
/// @author Daniel A. Nagy <daniel@ethdev.com>
|
||||||
contract chequebook is mortal {
|
contract chequebook is mortal {
|
||||||
// Cumulative paid amount in wei to each beneficiary
|
// Cumulative paid amount in wei to each beneficiary
|
||||||
mapping (address => uint256) sent;
|
mapping (address => uint256) public sent;
|
||||||
|
|
||||||
/// @notice Overdraft event
|
/// @notice Overdraft event
|
||||||
event Overdraft(address deadbeat);
|
event Overdraft(address deadbeat);
|
||||||
|
|
||||||
/// @notice Accessor for sent map
|
|
||||||
///
|
|
||||||
/// @param beneficiary beneficiary address
|
|
||||||
/// @return cumulative amount in wei sent to beneficiary
|
|
||||||
function getSent(address beneficiary) constant returns (uint256) {
|
|
||||||
return sent[beneficiary];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Cash cheque
|
/// @notice Cash cheque
|
||||||
///
|
///
|
||||||
/// @param beneficiary beneficiary address
|
/// @param beneficiary beneficiary address
|
||||||
5
swarm/services/chequebook/contract/contract.go
Normal file
5
swarm/services/chequebook/contract/contract.go
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// ContractDeployedCode is used to validate the contract
|
||||||
|
// this constant needs to be updated when the contract code is changed
|
||||||
|
package contract
|
||||||
|
|
||||||
|
const ContractDeployedCode = `0x606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156101fc57600054600160a060020a0316ff5b6100ab60043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100b5575b505050505050565b6060908152602090f35b30600160a060020a039081166c0100000000000000000000000090810260609081529188160260745260888690526048812080825260ff8616608090815260a086905260c0859052909260019260e0926020928290866161da5a03f11561000257505060405151600054600160a060020a03908116911614610136576100a3565b85600160a060020a031660006001600050600089600160a060020a03168152602001908152602001600020600050548703604051809050600060405180830381858888f19350505050156101b057846001600050600088600160a060020a03168152602001908152602001600020600050819055506100a3565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff5b56`
|
||||||
566
swarm/services/ens/contract/ens.go
Normal file
566
swarm/services/ens/contract/ens.go
Normal file
|
|
@ -0,0 +1,566 @@
|
||||||
|
// This file is an automatically generated Go binding. Do not modify as any
|
||||||
|
// change will likely be lost upon the next re-generation!
|
||||||
|
|
||||||
|
package contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ENSABI is the input ABI used to generate the binding from.
|
||||||
|
const ENSABI = `[{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"Owners","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"Registry","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"host","type":"bytes32"},{"name":"content","type":"bytes32"}],"name":"Set","outputs":[],"type":"function"}]`
|
||||||
|
|
||||||
|
// ENSBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const ENSBin = `0x606060405260008054600160a060020a03191633179055610148806100246000396000f3606060405260e060020a60003504633d14b257811461003c57806341c0e1b51461005d578063a0d03d3614610085578063be36e6761461009d575b005b61013c600435600260205260009081526040902054600160a060020a031681565b61003a60005433600160a060020a039081169116141561014657600054600160a060020a0316ff5b61013c60043560016020526000908152604090205481565b61003a600435602435600082815260026020526040812054600160a060020a031614156100e7576040600020805473ffffffffffffffffffffffffffffffffffffffff1916321790555b32600160a060020a03166002600050600084815260200190815260200160002060009054906101000a9004600160a060020a0316600160a060020a0316141561013857600160205260406000208190555b5050565b6060908152602090f35b56`
|
||||||
|
|
||||||
|
// DeployENS deploys a new Ethereum contract, binding an instance of ENS to it.
|
||||||
|
func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ENS, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(ENSABI))
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ENSBin), backend)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
return address, tx, &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENS is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type ENS struct {
|
||||||
|
ENSCaller // Read-only binding to the contract
|
||||||
|
ENSTransactor // Write-only binding to the contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENSCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type ENSCaller struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENSTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type ENSTransactor struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENSSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type ENSSession struct {
|
||||||
|
Contract *ENS // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENSCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type ENSCallerSession struct {
|
||||||
|
Contract *ENSCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENSTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type ENSTransactorSession struct {
|
||||||
|
Contract *ENSTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENSRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type ENSRaw struct {
|
||||||
|
Contract *ENS // Generic contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENSCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type ENSCallerRaw struct {
|
||||||
|
Contract *ENSCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENSTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type ENSTransactorRaw struct {
|
||||||
|
Contract *ENSTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewENS creates a new instance of ENS, bound to a specific deployed contract.
|
||||||
|
func NewENS(address common.Address, backend bind.ContractBackend) (*ENS, error) {
|
||||||
|
contract, err := bindENS(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewENSCaller creates a new read-only instance of ENS, bound to a specific deployed contract.
|
||||||
|
func NewENSCaller(address common.Address, caller bind.ContractCaller) (*ENSCaller, error) {
|
||||||
|
contract, err := bindENS(address, caller, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ENSCaller{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewENSTransactor creates a new write-only instance of ENS, bound to a specific deployed contract.
|
||||||
|
func NewENSTransactor(address common.Address, transactor bind.ContractTransactor) (*ENSTransactor, error) {
|
||||||
|
contract, err := bindENS(address, nil, transactor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ENSTransactor{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindENS binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindENS(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(ENSABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_ENS *ENSRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _ENS.Contract.ENSCaller.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_ENS *ENSRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _ENS.Contract.ENSTransactor.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_ENS *ENSRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _ENS.Contract.ENSTransactor.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_ENS *ENSCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _ENS.Contract.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_ENS *ENSTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _ENS.Contract.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_ENS *ENSTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _ENS.Contract.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owners is a free data retrieval call binding the contract method 0x3d14b257.
|
||||||
|
//
|
||||||
|
// Solidity: function Owners( bytes32) constant returns(address)
|
||||||
|
func (_ENS *ENSCaller) Owners(opts *bind.CallOpts, arg0 [32]byte) (common.Address, error) {
|
||||||
|
var (
|
||||||
|
ret0 = new(common.Address)
|
||||||
|
)
|
||||||
|
out := ret0
|
||||||
|
err := _ENS.contract.Call(opts, out, "Owners", arg0)
|
||||||
|
return *ret0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owners is a free data retrieval call binding the contract method 0x3d14b257.
|
||||||
|
//
|
||||||
|
// Solidity: function Owners( bytes32) constant returns(address)
|
||||||
|
func (_ENS *ENSSession) Owners(arg0 [32]byte) (common.Address, error) {
|
||||||
|
return _ENS.Contract.Owners(&_ENS.CallOpts, arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owners is a free data retrieval call binding the contract method 0x3d14b257.
|
||||||
|
//
|
||||||
|
// Solidity: function Owners( bytes32) constant returns(address)
|
||||||
|
func (_ENS *ENSCallerSession) Owners(arg0 [32]byte) (common.Address, error) {
|
||||||
|
return _ENS.Contract.Owners(&_ENS.CallOpts, arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registry is a free data retrieval call binding the contract method 0xa0d03d36.
|
||||||
|
//
|
||||||
|
// Solidity: function Registry( bytes32) constant returns(bytes32)
|
||||||
|
func (_ENS *ENSCaller) Registry(opts *bind.CallOpts, arg0 [32]byte) ([32]byte, error) {
|
||||||
|
var (
|
||||||
|
ret0 = new([32]byte)
|
||||||
|
)
|
||||||
|
out := ret0
|
||||||
|
err := _ENS.contract.Call(opts, out, "Registry", arg0)
|
||||||
|
return *ret0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registry is a free data retrieval call binding the contract method 0xa0d03d36.
|
||||||
|
//
|
||||||
|
// Solidity: function Registry( bytes32) constant returns(bytes32)
|
||||||
|
func (_ENS *ENSSession) Registry(arg0 [32]byte) ([32]byte, error) {
|
||||||
|
return _ENS.Contract.Registry(&_ENS.CallOpts, arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registry is a free data retrieval call binding the contract method 0xa0d03d36.
|
||||||
|
//
|
||||||
|
// Solidity: function Registry( bytes32) constant returns(bytes32)
|
||||||
|
func (_ENS *ENSCallerSession) Registry(arg0 [32]byte) ([32]byte, error) {
|
||||||
|
return _ENS.Contract.Registry(&_ENS.CallOpts, arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set is a paid mutator transaction binding the contract method 0xbe36e676.
|
||||||
|
//
|
||||||
|
// Solidity: function Set(host bytes32, content bytes32) returns()
|
||||||
|
func (_ENS *ENSTransactor) Set(opts *bind.TransactOpts, host [32]byte, content [32]byte) (*types.Transaction, error) {
|
||||||
|
return _ENS.contract.Transact(opts, "Set", host, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set is a paid mutator transaction binding the contract method 0xbe36e676.
|
||||||
|
//
|
||||||
|
// Solidity: function Set(host bytes32, content bytes32) returns()
|
||||||
|
func (_ENS *ENSSession) Set(host [32]byte, content [32]byte) (*types.Transaction, error) {
|
||||||
|
return _ENS.Contract.Set(&_ENS.TransactOpts, host, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set is a paid mutator transaction binding the contract method 0xbe36e676.
|
||||||
|
//
|
||||||
|
// Solidity: function Set(host bytes32, content bytes32) returns()
|
||||||
|
func (_ENS *ENSTransactorSession) Set(host [32]byte, content [32]byte) (*types.Transaction, error) {
|
||||||
|
return _ENS.Contract.Set(&_ENS.TransactOpts, host, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_ENS *ENSTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _ENS.contract.Transact(opts, "kill")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_ENS *ENSSession) Kill() (*types.Transaction, error) {
|
||||||
|
return _ENS.Contract.Kill(&_ENS.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_ENS *ENSTransactorSession) Kill() (*types.Transaction, error) {
|
||||||
|
return _ENS.Contract.Kill(&_ENS.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalABI is the input ABI used to generate the binding from.
|
||||||
|
const MortalABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"}]`
|
||||||
|
|
||||||
|
// MortalBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const MortalBin = `0x606060405260008054600160a060020a03191633179055605c8060226000396000f3606060405260e060020a600035046341c0e1b58114601a575b005b60186000543373ffffffffffffffffffffffffffffffffffffffff90811691161415605a5760005473ffffffffffffffffffffffffffffffffffffffff16ff5b56`
|
||||||
|
|
||||||
|
// DeployMortal deploys a new Ethereum contract, binding an instance of Mortal to it.
|
||||||
|
func DeployMortal(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Mortal, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(MortalABI))
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(MortalBin), backend)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
return address, tx, &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mortal is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type Mortal struct {
|
||||||
|
MortalCaller // Read-only binding to the contract
|
||||||
|
MortalTransactor // Write-only binding to the contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type MortalCaller struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type MortalTransactor struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type MortalSession struct {
|
||||||
|
Contract *Mortal // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type MortalCallerSession struct {
|
||||||
|
Contract *MortalCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type MortalTransactorSession struct {
|
||||||
|
Contract *MortalTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type MortalRaw struct {
|
||||||
|
Contract *Mortal // Generic contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type MortalCallerRaw struct {
|
||||||
|
Contract *MortalCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// MortalTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type MortalTransactorRaw struct {
|
||||||
|
Contract *MortalTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMortal creates a new instance of Mortal, bound to a specific deployed contract.
|
||||||
|
func NewMortal(address common.Address, backend bind.ContractBackend) (*Mortal, error) {
|
||||||
|
contract, err := bindMortal(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMortalCaller creates a new read-only instance of Mortal, bound to a specific deployed contract.
|
||||||
|
func NewMortalCaller(address common.Address, caller bind.ContractCaller) (*MortalCaller, error) {
|
||||||
|
contract, err := bindMortal(address, caller, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &MortalCaller{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMortalTransactor creates a new write-only instance of Mortal, bound to a specific deployed contract.
|
||||||
|
func NewMortalTransactor(address common.Address, transactor bind.ContractTransactor) (*MortalTransactor, error) {
|
||||||
|
contract, err := bindMortal(address, nil, transactor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &MortalTransactor{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindMortal binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindMortal(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(MortalABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Mortal *MortalRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Mortal.Contract.MortalCaller.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Mortal *MortalRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.MortalTransactor.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Mortal *MortalRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.MortalTransactor.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Mortal *MortalCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Mortal.Contract.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Mortal *MortalTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Mortal *MortalTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Mortal *MortalTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Mortal.contract.Transact(opts, "kill")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Mortal *MortalSession) Kill() (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.Kill(&_Mortal.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Mortal *MortalTransactorSession) Kill() (*types.Transaction, error) {
|
||||||
|
return _Mortal.Contract.Kill(&_Mortal.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedABI is the input ABI used to generate the binding from.
|
||||||
|
const OwnedABI = `[{"inputs":[],"type":"constructor"}]`
|
||||||
|
|
||||||
|
// OwnedBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const OwnedBin = `0x606060405260008054600160a060020a0319163317905560068060226000396000f3606060405200`
|
||||||
|
|
||||||
|
// DeployOwned deploys a new Ethereum contract, binding an instance of Owned to it.
|
||||||
|
func DeployOwned(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Owned, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(OwnedABI))
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(OwnedBin), backend)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
return address, tx, &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owned is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type Owned struct {
|
||||||
|
OwnedCaller // Read-only binding to the contract
|
||||||
|
OwnedTransactor // Write-only binding to the contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedCaller struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedTransactor struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type OwnedSession struct {
|
||||||
|
Contract *Owned // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type OwnedCallerSession struct {
|
||||||
|
Contract *OwnedCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type OwnedTransactorSession struct {
|
||||||
|
Contract *OwnedTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type OwnedRaw struct {
|
||||||
|
Contract *Owned // Generic contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedCallerRaw struct {
|
||||||
|
Contract *OwnedCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnedTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedTransactorRaw struct {
|
||||||
|
Contract *OwnedTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOwned creates a new instance of Owned, bound to a specific deployed contract.
|
||||||
|
func NewOwned(address common.Address, backend bind.ContractBackend) (*Owned, error) {
|
||||||
|
contract, err := bindOwned(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOwnedCaller creates a new read-only instance of Owned, bound to a specific deployed contract.
|
||||||
|
func NewOwnedCaller(address common.Address, caller bind.ContractCaller) (*OwnedCaller, error) {
|
||||||
|
contract, err := bindOwned(address, caller, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &OwnedCaller{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOwnedTransactor creates a new write-only instance of Owned, bound to a specific deployed contract.
|
||||||
|
func NewOwnedTransactor(address common.Address, transactor bind.ContractTransactor) (*OwnedTransactor, error) {
|
||||||
|
contract, err := bindOwned(address, nil, transactor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &OwnedTransactor{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindOwned binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindOwned(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(OwnedABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Owned *OwnedRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Owned.Contract.OwnedCaller.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Owned *OwnedRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Owned.Contract.OwnedTransactor.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Owned *OwnedRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Owned.Contract.OwnedTransactor.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Owned *OwnedCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _Owned.Contract.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Owned *OwnedTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _Owned.Contract.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Owned *OwnedTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _Owned.Contract.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
19
swarm/services/ens/contract/ens.sol
Normal file
19
swarm/services/ens/contract/ens.sol
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import "mortal";
|
||||||
|
/// @title Swarm Distributed Preimage Archive
|
||||||
|
/// @author Viktor Tron <viktor@ethdev.com>
|
||||||
|
contract ENS is mortal
|
||||||
|
{
|
||||||
|
|
||||||
|
mapping (bytes32 => bytes32) public Registry;
|
||||||
|
mapping (bytes32 => address) public Owners;
|
||||||
|
|
||||||
|
function Set(bytes32 host, bytes32 content) {
|
||||||
|
if (Owners[host] == 0x0) {
|
||||||
|
Owners[host] = tx.origin;
|
||||||
|
}
|
||||||
|
if (Owners[host] == tx.origin) {
|
||||||
|
Registry[host] = content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
84
swarm/services/ens/ens.go
Normal file
84
swarm/services/ens/ens.go
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
//go:generate abigen --sol contract/ens.sol --pkg contract --out contract/ens.go
|
||||||
|
package ens
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/ens/contract"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var domainAndVersion = regexp.MustCompile("[@:;,]+")
|
||||||
|
|
||||||
|
// swarm domain name registry and resolver
|
||||||
|
// the ENS instance can be directly wrapped in rpc.Api
|
||||||
|
type ENS struct {
|
||||||
|
*contract.ENSSession
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewENS creates a proxy instance wrapping the abigen interface to the ENS contract
|
||||||
|
// using the transaction options passed as first argument, it sets up a session
|
||||||
|
func NewENS(transactOpts *bind.TransactOpts, contractAddr common.Address, contractBackend bind.ContractBackend) *ENS {
|
||||||
|
ens, err := contract.NewENS(contractAddr, contractBackend)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(logger.Debug).Infof("error setting up name server on %v, skipping: %v", contractAddr.Hex(), err)
|
||||||
|
}
|
||||||
|
return &ENS{
|
||||||
|
&contract.ENSSession{
|
||||||
|
Contract: ens,
|
||||||
|
TransactOpts: *transactOpts,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register(name, hash )
|
||||||
|
//involves sending a transaction (sent by sender specified as From of Transact)
|
||||||
|
func (self *ENS) Register(name string, hash common.Hash) (*types.Transaction, error) {
|
||||||
|
namehash := crypto.Sha3Hash([]byte(name))
|
||||||
|
owner, err := self.Owners(namehash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error registering '%s': %v", name, err)
|
||||||
|
}
|
||||||
|
if (owner != common.Address{} && owner != self.TransactOpts.From) {
|
||||||
|
return nil, fmt.Errorf("error registering '%s': already set as %", name)
|
||||||
|
}
|
||||||
|
glog.V(logger.Debug).Infof("[ENS]: host '%s' (hash: '%v') to be registered as '%v'", name, namehash.Hex(), hash.Hex())
|
||||||
|
return self.Set(namehash, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ENS) WhoseIs(name string) (common.Address, error) {
|
||||||
|
namehash := crypto.Sha3Hash([]byte(name))
|
||||||
|
return self.Owners(namehash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve is a non-tranasctional call, returns hash as storage.Key
|
||||||
|
func (self *ENS) Resolve(hostPort string) (storage.Key, error) {
|
||||||
|
host := hostPort
|
||||||
|
var version *big.Int
|
||||||
|
parts := domainAndVersion.Split(host, 3)
|
||||||
|
if len(parts) > 1 && parts[1] != "" {
|
||||||
|
host = parts[0]
|
||||||
|
version = common.Big(parts[1])
|
||||||
|
}
|
||||||
|
hash := crypto.Sha3Hash([]byte(host))
|
||||||
|
_ = version
|
||||||
|
// hash, err = self.registrar.Resolver(version).HashToHash(hostHash)
|
||||||
|
hash, err := self.Registry(hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error resolving '%v': %v", hash.Hex(), err)
|
||||||
|
}
|
||||||
|
if (hash == common.Hash{}) {
|
||||||
|
return nil, fmt.Errorf("unable to resolve '%v': not found", hash)
|
||||||
|
}
|
||||||
|
contentHash := storage.Key(hash.Bytes())
|
||||||
|
glog.V(logger.Debug).Infof("[ENS] resolve host '%v' to contentHash: '%v'", hash, contentHash)
|
||||||
|
return contentHash, nil
|
||||||
|
}
|
||||||
56
swarm/services/ens/ens_test.go
Normal file
56
swarm/services/ens/ens_test.go
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
package ens
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/ens/contract"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
name = "my name on ENS"
|
||||||
|
hash = crypto.Sha3Hash([]byte("my content"))
|
||||||
|
addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) {
|
||||||
|
deployTransactor := bind.NewKeyedTransactor(prvKey)
|
||||||
|
deployTransactor.Value = amount
|
||||||
|
addr, _, _, err := contract.DeployENS(deployTransactor, backend)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
|
backend.Commit()
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestENS(t *testing.T) {
|
||||||
|
contractBackend := backends.NewSimulatedBackend(core.GenesisAccount{addr, big.NewInt(1000000000)})
|
||||||
|
transactOpts := bind.NewKeyedTransactor(key)
|
||||||
|
contractAddr, err := deploy(key, big.NewInt(0), contractBackend)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
ens := NewENS(transactOpts, contractAddr, contractBackend)
|
||||||
|
_, err = ens.Register(name, hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
contractBackend.Commit()
|
||||||
|
vhost, err := ens.Resolve(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
if vhost.Hex() != hash.Hex()[2:] {
|
||||||
|
t.Fatalf("resolve error, expected %v, got %v", hash.Hex(), vhost)
|
||||||
|
// t.Fatalf("resolve error, expected %v, got %v", transactOpts.From, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -9,12 +9,15 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/chequebook"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/common/swap"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/chequebook"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/chequebook/contract"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/swap/swap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SwAP Swarm Accounting Protocol with
|
// SwAP Swarm Accounting Protocol with
|
||||||
|
|
@ -31,14 +34,9 @@ var (
|
||||||
autoDepositBuffer = big.NewInt(100000000000000) // buffer that is surplus for fork protection etc (wei)
|
autoDepositBuffer = big.NewInt(100000000000000) // buffer that is surplus for fork protection etc (wei)
|
||||||
buyAt = big.NewInt(20000000000) // maximum chunk price host is willing to pay (wei)
|
buyAt = big.NewInt(20000000000) // maximum chunk price host is willing to pay (wei)
|
||||||
sellAt = big.NewInt(20000000000) // minimum chunk price host requires (wei)
|
sellAt = big.NewInt(20000000000) // minimum chunk price host requires (wei)
|
||||||
payAt = 100 // threshold that triggers payment request (units)
|
payAt = 100 // threshold that triggers payment {request} (units)
|
||||||
dropAt = 10000 // threshold that triggers disconnect (units)
|
dropAt = 10000 // threshold that triggers disconnect (units)
|
||||||
|
|
||||||
maxRetries = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
retryInterval = 10 * time.Second
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SwapParams struct {
|
type SwapParams struct {
|
||||||
|
|
@ -52,15 +50,15 @@ type SwapProfile struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PayProfile struct {
|
type PayProfile struct {
|
||||||
PublicKey string // check againsst signature of promise
|
PublicKey string // check against signature of promise
|
||||||
Contract common.Address // address of chequebook contract
|
Contract common.Address // address of chequebook contract
|
||||||
Beneficiary common.Address // recipient address for swarm sales revenue
|
Beneficiary common.Address // recipient address for swarm sales revenue
|
||||||
privateKey *ecdsa.PrivateKey `json:"-"`
|
privateKey *ecdsa.PrivateKey
|
||||||
publicKey *ecdsa.PublicKey `json:"-"`
|
publicKey *ecdsa.PublicKey
|
||||||
owner common.Address
|
owner common.Address
|
||||||
chbook *chequebook.Chequebook `json:"-"`
|
chbook *chequebook.Chequebook
|
||||||
backend chequebook.Backend
|
// backend bind.Backend
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultSwapParams(contract common.Address, prvkey *ecdsa.PrivateKey) *SwapParams {
|
func DefaultSwapParams(contract common.Address, prvkey *ecdsa.PrivateKey) *SwapParams {
|
||||||
|
|
@ -101,18 +99,18 @@ func DefaultSwapParams(contract common.Address, prvkey *ecdsa.PrivateKey) *SwapP
|
||||||
// n < 0 called when receiving chunks = receiving delivery responses
|
// n < 0 called when receiving chunks = receiving delivery responses
|
||||||
// OR receiving cheques.
|
// OR receiving cheques.
|
||||||
|
|
||||||
func NewSwap(local *SwapParams, remote *SwapProfile, proto swap.Protocol) (self *swap.Swap, err error) {
|
func NewSwap(local *SwapParams, remote *SwapProfile, backend bind.Backend, proto swap.Protocol) (self *swap.Swap, err error) {
|
||||||
|
|
||||||
// check if remote chequebook is valid
|
// check if remote chequebook is valid
|
||||||
// insolvent chequebooks suicide so will signal as invalid
|
// insolvent chequebooks suicide so will signal as invalid
|
||||||
// TODO: monitoring a chequebooks events
|
// TODO: monitoring a chequebooks events
|
||||||
var in *chequebook.Inbox
|
var in *chequebook.Inbox
|
||||||
err = chequebook.Validate(remote.Contract, local.backend)
|
err = bind.Validate(remote.Contract, contract.ContractDeployedCode, backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Info).Infof("[BZZ] SWAP invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err)
|
glog.V(logger.Info).Infof("[BZZ] SWAP invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err)
|
||||||
} else {
|
} else {
|
||||||
// remote contract valid, create inbox
|
// remote contract valid, create inbox
|
||||||
in, err = chequebook.NewInbox(remote.Contract, local.owner, local.Beneficiary, crypto.ToECDSAPub(common.FromHex(remote.PublicKey)), local.backend)
|
in, err = chequebook.NewInbox(local.privateKey, remote.Contract, local.Beneficiary, crypto.ToECDSAPub(common.FromHex(remote.PublicKey)), backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infof("[BZZ] SWAP unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err)
|
glog.V(logger.Warn).Infof("[BZZ] SWAP unable to set up inbox for chequebook contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err)
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +118,7 @@ func NewSwap(local *SwapParams, remote *SwapProfile, proto swap.Protocol) (self
|
||||||
|
|
||||||
// cheque if local chequebook contract is valid
|
// cheque if local chequebook contract is valid
|
||||||
var out *chequebook.Outbox
|
var out *chequebook.Outbox
|
||||||
err = chequebook.Validate(local.Contract, local.backend)
|
err = bind.Validate(local.Contract, contract.ContractDeployedCode, backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infof("[BZZ] SWAP unable to set up outbox for peer %v: chequebook contract (owner: %v): %v)", proto, local.owner.Hex(), err)
|
glog.V(logger.Warn).Infof("[BZZ] SWAP unable to set up outbox for peer %v: chequebook contract (owner: %v): %v)", proto, local.owner.Hex(), err)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -161,35 +159,33 @@ func (self *SwapParams) Chequebook() *chequebook.Chequebook {
|
||||||
return self.chbook
|
return self.chbook
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func (self *SwapParams) Backend() bind.Backend {
|
||||||
|
// return self.backend
|
||||||
|
// }
|
||||||
|
|
||||||
func (self *SwapParams) PrivateKey() *ecdsa.PrivateKey {
|
func (self *SwapParams) PrivateKey() *ecdsa.PrivateKey {
|
||||||
return self.privateKey
|
return self.privateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SwapParams) PublicKey() *ecdsa.PublicKey {
|
// func (self *SwapParams) PublicKey() *ecdsa.PublicKey {
|
||||||
return self.publicKey
|
// return self.publicKey
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (self *SwapParams) SetKey(prvkey *ecdsa.PrivateKey) {
|
func (self *SwapParams) SetKey(prvkey *ecdsa.PrivateKey) {
|
||||||
self.privateKey = prvkey
|
self.privateKey = prvkey
|
||||||
self.publicKey = &prvkey.PublicKey
|
self.publicKey = &prvkey.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
confirmationInterval = 60000000000
|
|
||||||
timeout = 30000000000 // 30 sec
|
|
||||||
)
|
|
||||||
|
|
||||||
// setChequebook(path, backend) wraps the
|
// setChequebook(path, backend) wraps the
|
||||||
// chequebook initialiser and sets up autoDeposit to cover spending.
|
// chequebook initialiser and sets up autoDeposit to cover spending.
|
||||||
func (self *SwapParams) SetChequebook(path string, backend chequebook.Backend) (done chan bool, err error) {
|
func (self *SwapParams) SetChequebook(path string, backend bind.Backend) (done chan bool, err error) {
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
var valid bool
|
var valid bool
|
||||||
done = make(chan bool)
|
done = make(chan bool)
|
||||||
self.backend = backend
|
err = bind.Validate(self.Contract, contract.ContractDeployedCode, backend)
|
||||||
err = chequebook.Validate(self.Contract, backend)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
owner := crypto.PubkeyToAddress(*(self.publicKey))
|
go self.deployChequebook(path, backend, done)
|
||||||
go self.deployChequebook(owner, path, done)
|
|
||||||
} else {
|
} else {
|
||||||
valid = true
|
valid = true
|
||||||
go func() {
|
go func() {
|
||||||
|
|
@ -204,40 +200,39 @@ func (self *SwapParams) SetChequebook(path string, backend chequebook.Backend) (
|
||||||
return done, nil
|
return done, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SwapParams) deployChequebook(owner common.Address, path string, done chan bool) {
|
func deploy(deployTransactor *bind.TransactOpts, backend bind.ContractBackend) (*types.Transaction, error) {
|
||||||
var timer = time.NewTimer(0).C
|
_, tx, _, err := contract.DeployChequebook(deployTransactor, backend)
|
||||||
retries := 0
|
if err != nil {
|
||||||
var err error
|
return nil, err
|
||||||
|
}
|
||||||
|
return tx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prvKey *ecdsa.PrivateKey, amount *big.Int,
|
||||||
|
|
||||||
|
func (self *SwapParams) deployChequebook(path string, backend bind.Backend, done chan bool) {
|
||||||
|
owner := crypto.PubkeyToAddress(*(self.publicKey))
|
||||||
|
glog.V(logger.Info).Infof("[BZZ] SWAP Deploying new chequebook (owner: %v)", owner.Hex())
|
||||||
var valid bool
|
var valid bool
|
||||||
OUT:
|
|
||||||
for {
|
transactOpts := bind.NewKeyedTransactor(self.privateKey)
|
||||||
select {
|
transactOpts.Value = self.AutoDepositBuffer
|
||||||
case <-timer:
|
contract, err := bind.Deploy(deploy, contract.ContractDeployedCode, transactOpts, bind.DefaultDeployOptions(), backend)
|
||||||
// this is blocking
|
if err != nil {
|
||||||
glog.V(logger.Info).Infof("[BZZ] SWAP Deploying new chequebook (owner: %v)", owner.Hex())
|
glog.V(logger.Error).Infof("[BZZ] SWAP unable to deploy new chequebook: %v", err)
|
||||||
var contract common.Address
|
return
|
||||||
contract, err = chequebook.Deploy(owner, self.backend, self.AutoDepositBuffer, confirmationInterval, timeout)
|
}
|
||||||
if err != nil {
|
|
||||||
glog.V(logger.Info).Infof("[BZZ] SWAP unable to deploy new chequebook: %v...retrying in %v", err, retryInterval)
|
// need to save config at this point
|
||||||
if retries >= maxRetries {
|
self.lock.Lock()
|
||||||
glog.V(logger.Info).Infof("[BZZ] SWAP unable to deploy new chequebook: giving up after %v retries", retries)
|
self.Contract = contract
|
||||||
break OUT
|
err = self.newChequebookFromContract(path, backend)
|
||||||
}
|
self.lock.Unlock()
|
||||||
retries++
|
if err != nil {
|
||||||
timer = time.NewTicker(retryInterval).C
|
glog.V(logger.Warn).Infof("[BZZ] SWAP error initialising cheque book (owner: %v): %v", owner.Hex(), err)
|
||||||
} else {
|
} else {
|
||||||
// need to save config at this point
|
valid = true
|
||||||
self.lock.Lock()
|
glog.V(logger.Info).Infof("[BZZ] SWAP new chequebook deployed at %v (owner: %v)", contract.Hex(), owner.Hex())
|
||||||
self.Contract = contract
|
|
||||||
err = self.newChequebookFromContract(path, self.backend)
|
|
||||||
if err != nil {
|
|
||||||
glog.V(logger.Info).Infof("[BZZ] SWAP error initialising cheque book (owner: %v)", owner.Hex())
|
|
||||||
}
|
|
||||||
self.lock.Unlock()
|
|
||||||
valid = true
|
|
||||||
break OUT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
done <- valid
|
done <- valid
|
||||||
close(done)
|
close(done)
|
||||||
|
|
@ -245,8 +240,7 @@ OUT:
|
||||||
|
|
||||||
// initialise the chequebook from a persisted json file or create a new one
|
// initialise the chequebook from a persisted json file or create a new one
|
||||||
// caller holds the lock
|
// caller holds the lock
|
||||||
func (self *SwapParams) newChequebookFromContract(path string, backend chequebook.Backend) error {
|
func (self *SwapParams) newChequebookFromContract(path string, backend bind.Backend) error {
|
||||||
|
|
||||||
hexkey := common.Bytes2Hex(self.Contract.Bytes())
|
hexkey := common.Bytes2Hex(self.Contract.Bytes())
|
||||||
err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm)
|
err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -254,7 +248,7 @@ func (self *SwapParams) newChequebookFromContract(path string, backend chequeboo
|
||||||
}
|
}
|
||||||
|
|
||||||
chbookpath := filepath.Join(path, "chequebooks", hexkey+".json")
|
chbookpath := filepath.Join(path, "chequebooks", hexkey+".json")
|
||||||
self.chbook, err = chequebook.LoadChequebook(chbookpath, self.privateKey, backend)
|
self.chbook, err = chequebook.LoadChequebook(chbookpath, self.privateKey, backend, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.chbook, err = chequebook.NewChequebook(chbookpath, self.Contract, self.privateKey, backend)
|
self.chbook, err = chequebook.NewChequebook(chbookpath, self.Contract, self.privateKey, backend)
|
||||||
|
|
|
||||||
|
|
@ -152,13 +152,13 @@ func (self *Swap) Add(n int) error {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
self.balance += n
|
self.balance += n
|
||||||
if !self.Sells && self.balance > 0 {
|
if !self.Sells && self.balance > 0 {
|
||||||
glog.V(logger.Detail).Infof("[SWAP] <%v> remote peer cannot have debt (unable to buy)", self.proto, self.balance)
|
glog.V(logger.Detail).Infof("[SWAP] <%v> remote peer cannot have debt (balance: %v)", self.proto, self.balance)
|
||||||
self.proto.Drop()
|
self.proto.Drop()
|
||||||
return fmt.Errorf("[SWAP] <%v> remote peer cannot have debt (unable to buy)", self.proto, self.balance)
|
return fmt.Errorf("[SWAP] <%v> remote peer cannot have debt (balance: %v)", self.proto, self.balance)
|
||||||
}
|
}
|
||||||
if !self.Buys && self.balance < 0 {
|
if !self.Buys && self.balance < 0 {
|
||||||
glog.V(logger.Detail).Infof("[SWAP] <%v> we cannot have debt (unable to buy)", self.proto, self.balance)
|
glog.V(logger.Detail).Infof("[SWAP] <%v> we cannot have debt (balance: %v)", self.proto, self.balance)
|
||||||
return fmt.Errorf("[SWAP] <%v> we cannot have debt (unable to buy)", self.proto, self.balance)
|
return fmt.Errorf("[SWAP] <%v> we cannot have debt (balance: %v)", self.proto, self.balance)
|
||||||
}
|
}
|
||||||
if self.balance >= int(self.local.DropAt) {
|
if self.balance >= int(self.local.DropAt) {
|
||||||
glog.V(logger.Detail).Infof("[SWAP] <%v> remote peer has too much debt (balance: %v, disconnect threshold: %v)", self.proto, self.balance, self.local.DropAt)
|
glog.V(logger.Detail).Infof("[SWAP] <%v> remote peer has too much debt (balance: %v, disconnect threshold: %v)", self.proto, self.balance, self.local.DropAt)
|
||||||
|
|
@ -17,7 +17,7 @@ contract Swarm
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping (address => Bee) swarm;
|
mapping (address => Bee) swarm;
|
||||||
|
|
||||||
// block number of transactions presenting chunks
|
// block number of transactions presenting chunks
|
||||||
mapping (bytes32 => uint) presentedChunks;
|
mapping (bytes32 => uint) presentedChunks;
|
||||||
|
|
||||||
|
|
@ -111,7 +111,7 @@ contract Swarm
|
||||||
b.reporter = msg.sender;
|
b.reporter = msg.sender;
|
||||||
Report(signer);
|
Report(signer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Present a chunk in order to avoid losing deposit.
|
/// @notice Present a chunk in order to avoid losing deposit.
|
||||||
///
|
///
|
||||||
/// @param chunk chunk data
|
/// @param chunk chunk data
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ func (self *TreeChunker) KeySize() int64 {
|
||||||
|
|
||||||
// String() for pretty printing
|
// String() for pretty printing
|
||||||
func (self *Chunk) String() string {
|
func (self *Chunk) String() string {
|
||||||
return fmt.Sprintf("Key: %v TreeSize: %v Chunksize: %v\n", self.Key.Log(), self.Size, len(self.SData))
|
return fmt.Sprintf("Key: %v TreeSize: %v Chunksize: %v", self.Key.Log(), self.Size, len(self.SData))
|
||||||
}
|
}
|
||||||
|
|
||||||
// The treeChunkers own Hash hashes together
|
// The treeChunkers own Hash hashes together
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ package storage
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/compression/rle"
|
"github.com/ethereum/go-ethereum/compression/rle"
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||||
|
|
@ -82,15 +81,3 @@ func (self *LDBDatabase) Close() {
|
||||||
// Close the leveldb database
|
// Close the leveldb database
|
||||||
self.db.Close()
|
self.db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LDBDatabase) Print() {
|
|
||||||
iter := self.db.NewIterator(nil, nil)
|
|
||||||
for iter.Next() {
|
|
||||||
key := iter.Key()
|
|
||||||
value := iter.Value()
|
|
||||||
|
|
||||||
fmt.Printf("%x(%d): ", key, len(key))
|
|
||||||
node := common.NewValueFromBytes(value)
|
|
||||||
fmt.Printf("%v\n", node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
159
swarm/swarm.go
159
swarm/swarm.go
|
|
@ -2,12 +2,14 @@ package swarm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/chequebook"
|
|
||||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||||
"github.com/ethereum/go-ethereum/common/registrar/ethreg"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
|
@ -15,27 +17,62 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/swarm/api"
|
"github.com/ethereum/go-ethereum/swarm/api"
|
||||||
|
httpapi "github.com/ethereum/go-ethereum/swarm/api/http"
|
||||||
"github.com/ethereum/go-ethereum/swarm/network"
|
"github.com/ethereum/go-ethereum/swarm/network"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/chequebook"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/services/ens"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Namespace = "bzz"
|
||||||
|
Version = "0.1" // versioning reflect POC and release versions
|
||||||
|
)
|
||||||
|
|
||||||
|
var ENSContractAddr = common.HexToAddress("0x504cdf3992d8f81a4182bd7b24e270d3a28711e3")
|
||||||
|
|
||||||
// the swarm stack
|
// the swarm stack
|
||||||
type Swarm struct {
|
type Swarm struct {
|
||||||
config *api.Config // swarm configuration
|
ethereum *eth.Ethereum
|
||||||
api *api.Api // high level api layer (fs/manifest)
|
config *api.Config // swarm configuration
|
||||||
dbAccess *network.DbAccess // access to local chunk db iterator and storage counter
|
api *api.Api // high level api layer (fs/manifest)
|
||||||
storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends
|
dns api.Resolver // DNS registrar
|
||||||
dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support
|
dbAccess *network.DbAccess // access to local chunk db iterator and storage counter
|
||||||
depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage
|
storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends
|
||||||
cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud)
|
dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support
|
||||||
hive *network.Hive // the logistic manager
|
depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage
|
||||||
client *httpclient.HTTPClient // bzz capable light http client
|
cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud)
|
||||||
|
hive *network.Hive // the logistic manager
|
||||||
|
client *httpclient.HTTPClient // bzz capable light http client
|
||||||
|
backend bind.Backend // simple blockchain Backend
|
||||||
|
privateKey *ecdsa.PrivateKey
|
||||||
|
swapEnabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type SwarmAPI struct {
|
||||||
|
Api *api.Api
|
||||||
|
Backend bind.Backend
|
||||||
|
PrvKey *ecdsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Swarm) API() *SwarmAPI {
|
||||||
|
return &SwarmAPI{
|
||||||
|
Api: self.api,
|
||||||
|
Backend: self.backend,
|
||||||
|
PrvKey: self.privateKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Backend interface {
|
||||||
|
GetTxReceipt(txhash common.Hash) *types.Receipt
|
||||||
|
BalanceAt(address common.Address) *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a new swarm service instance
|
// creates a new swarm service instance
|
||||||
// implements node.Service
|
// implements node.Service
|
||||||
func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool) (self *Swarm, err error) {
|
func NewSwarm(ctx *node.ServiceContext, config *api.Config, swapEnabled, syncEnabled bool) (self *Swarm, err error) {
|
||||||
|
|
||||||
if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) {
|
if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) {
|
||||||
return nil, fmt.Errorf("empty public key")
|
return nil, fmt.Errorf("empty public key")
|
||||||
|
|
@ -45,21 +82,25 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ethereum *eth.Ethereum
|
var ethereum *eth.Ethereum
|
||||||
if err := stack.Service(ðereum); err != nil {
|
if err := ctx.Service(ðereum); err != nil {
|
||||||
return nil, fmt.Errorf("unable to find Ethereum service: %v", err)
|
return nil, fmt.Errorf("unable to find Ethereum service: %v", err)
|
||||||
}
|
}
|
||||||
self = &Swarm{
|
self = &Swarm{
|
||||||
config: config,
|
config: config,
|
||||||
client: ethereum.HTTPClient(),
|
ethereum: ethereum,
|
||||||
|
swapEnabled: swapEnabled,
|
||||||
|
client: ethereum.HTTPClient(),
|
||||||
|
privateKey: config.Swap.PrivateKey(),
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Setting up Swarm service components")
|
glog.V(logger.Debug).Infof("[BZZ] Setting up Swarm service components")
|
||||||
|
|
||||||
// setup local store
|
|
||||||
hash := storage.MakeHashFunc(config.ChunkerParams.Hash)
|
hash := storage.MakeHashFunc(config.ChunkerParams.Hash)
|
||||||
lstore, err := storage.NewLocalStore(hash, config.StoreParams)
|
lstore, err := storage.NewLocalStore(hash, config.StoreParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup local store
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Set up local storage")
|
glog.V(logger.Debug).Infof("[BZZ] Set up local storage")
|
||||||
|
|
||||||
self.dbAccess = network.NewDbAccess(lstore)
|
self.dbAccess = network.NewDbAccess(lstore)
|
||||||
|
|
@ -69,6 +110,8 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool)
|
||||||
self.hive = network.NewHive(
|
self.hive = network.NewHive(
|
||||||
common.HexToHash(self.config.BzzKey), // key to hive (kademlia base address)
|
common.HexToHash(self.config.BzzKey), // key to hive (kademlia base address)
|
||||||
config.HiveParams, // configuration parameters
|
config.HiveParams, // configuration parameters
|
||||||
|
swapEnabled, // SWAP enabled
|
||||||
|
syncEnabled, // syncronisation enabled
|
||||||
)
|
)
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Set up swarm network with Kademlia hive")
|
glog.V(logger.Debug).Infof("[BZZ] Set up swarm network with Kademlia hive")
|
||||||
|
|
||||||
|
|
@ -78,9 +121,9 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool)
|
||||||
// setup cloud storage internal access layer
|
// setup cloud storage internal access layer
|
||||||
|
|
||||||
self.storage = storage.NewNetStore(hash, lstore, cloud, config.StoreParams)
|
self.storage = storage.NewNetStore(hash, lstore, cloud, config.StoreParams)
|
||||||
glog.V(logger.Debug).Infof("[BZZ] -> Level 0: swarm net store shared access layer to Swarm Chunk Store")
|
glog.V(logger.Debug).Infof("[BZZ] -> swarm net store shared access layer to Swarm Chunk Store")
|
||||||
|
|
||||||
// set up Depo (storage handler = remote cloud storage access layer)
|
// set up Depo (storage handler = cloud storage access layer for incoming remote requests)
|
||||||
self.depo = network.NewDepo(hash, lstore, self.storage)
|
self.depo = network.NewDepo(hash, lstore, self.storage)
|
||||||
glog.V(logger.Debug).Infof("[BZZ] -> REmote Access to CHunks")
|
glog.V(logger.Debug).Infof("[BZZ] -> REmote Access to CHunks")
|
||||||
|
|
||||||
|
|
@ -89,23 +132,22 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool)
|
||||||
glog.V(logger.Debug).Infof("[BZZ] -> Local Access to Swarm")
|
glog.V(logger.Debug).Infof("[BZZ] -> Local Access to Swarm")
|
||||||
// Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage
|
// Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage
|
||||||
self.dpa = storage.NewDPA(dpaChunkStore, self.config.ChunkerParams)
|
self.dpa = storage.NewDPA(dpaChunkStore, self.config.ChunkerParams)
|
||||||
glog.V(logger.Debug).Infof("[BZZ] -> Level 1: Document/File API")
|
glog.V(logger.Debug).Infof("[BZZ] -> Content Store API")
|
||||||
|
|
||||||
|
// set up blockchain and contract interface bindings
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] -> native backend for abigen contract bindings")
|
||||||
|
self.backend = eth.NewContractBackend(ethereum)
|
||||||
|
|
||||||
// set up high level api
|
// set up high level api
|
||||||
backend := api.NewEthApi(ethereum)
|
transactOpts := bind.NewKeyedTransactor(self.privateKey)
|
||||||
backend.UpdateState()
|
// backend := ethereum.ContractBackend()
|
||||||
self.api = api.NewApi(self.dpa, ethreg.New(backend), self.config)
|
self.dns = ens.NewENS(transactOpts, ENSContractAddr, self.backend)
|
||||||
// Manifests for Smart Hosting
|
glog.V(logger.Debug).Infof("[BZZ] -> Swarm Domain Name Registrar")
|
||||||
glog.V(logger.Debug).Infof("[BZZ] -> Level 2: Collection/Directory API")
|
|
||||||
|
self.api = api.NewApi(self.dpa, self.dns)
|
||||||
|
// Manifests for Smart Hosting
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] -> Web3 virtual server API")
|
||||||
|
|
||||||
// set chequebook
|
|
||||||
if swapEnabled {
|
|
||||||
err = self.SetChequebook(backend)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Unable to set chequebook for SWAP: %v", err)
|
|
||||||
}
|
|
||||||
glog.V(logger.Debug).Infof("[BZZ] -> cheque book for SWAP")
|
|
||||||
}
|
|
||||||
return self, nil
|
return self, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,6 +171,16 @@ func (self *Swarm) Start(net *p2p.Server) error {
|
||||||
net.AddPeer(node)
|
net.AddPeer(node)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// set chequebook
|
||||||
|
if self.swapEnabled {
|
||||||
|
err := self.SetChequebook()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to set chequebook for SWAP: %v", err)
|
||||||
|
}
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] -> cheque book for SWAP: %v", self.config.Swap.Chequebook())
|
||||||
|
} else {
|
||||||
|
glog.V(logger.Debug).Infof("[BZZ] SWAP disabled: no cheque book set")
|
||||||
|
}
|
||||||
|
|
||||||
glog.V(logger.Warn).Infof("[BZZ] Starting Swarm service")
|
glog.V(logger.Warn).Infof("[BZZ] Starting Swarm service")
|
||||||
self.hive.Start(
|
self.hive.Start(
|
||||||
|
|
@ -143,7 +195,7 @@ func (self *Swarm) Start(net *p2p.Server) error {
|
||||||
|
|
||||||
// start swarm http proxy server
|
// start swarm http proxy server
|
||||||
if self.config.Port != "" {
|
if self.config.Port != "" {
|
||||||
go api.StartHttpServer(self.api, self.config.Port)
|
go httpapi.StartHttpServer(self.api, self.config.Port)
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm http proxy started on port: %v", self.config.Port)
|
glog.V(logger.Debug).Infof("[BZZ] Swarm http proxy started on port: %v", self.config.Port)
|
||||||
|
|
||||||
|
|
@ -154,7 +206,7 @@ func (self *Swarm) Start(net *p2p.Server) error {
|
||||||
"bzz": self.config.Port,
|
"bzz": self.config.Port,
|
||||||
}
|
}
|
||||||
for scheme, port := range schemes {
|
for scheme, port := range schemes {
|
||||||
self.client.RegisterScheme(scheme, &api.RoundTripper{Port: port})
|
self.client.RegisterScheme(scheme, &httpapi.RoundTripper{Port: port})
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("[BZZ] Swarm protocol handlers registered for url schemes: %v", schemes)
|
glog.V(logger.Debug).Infof("[BZZ] Swarm protocol handlers registered for url schemes: %v", schemes)
|
||||||
|
|
||||||
|
|
@ -175,27 +227,44 @@ func (self *Swarm) Stop() error {
|
||||||
|
|
||||||
// implements the node.Service interface
|
// implements the node.Service interface
|
||||||
func (self *Swarm) Protocols() []p2p.Protocol {
|
func (self *Swarm) Protocols() []p2p.Protocol {
|
||||||
proto, err := network.Bzz(self.depo, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams)
|
proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return []p2p.Protocol{proto}
|
return []p2p.Protocol{proto}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// implements node.Service
|
||||||
|
// Apis returns the RPC Api descriptors the Swarm implementation offers
|
||||||
|
func (self *Swarm) APIs() []rpc.API {
|
||||||
|
return []rpc.API{
|
||||||
|
// public APIs.
|
||||||
|
rpc.API{Namespace, Version, api.NewStorage(self.api), true},
|
||||||
|
rpc.API{"ens", Version, self.dns, true},
|
||||||
|
rpc.API{Namespace, Version, &Info{self.config, chequebook.ContractParams}, true},
|
||||||
|
// admin APIs
|
||||||
|
rpc.API{Namespace, Version, api.NewFileSystem(self.api), false},
|
||||||
|
rpc.API{Namespace, Version, api.NewControl(self.api, self.hive), false},
|
||||||
|
// rpc.API{Namespace, Version, api.NewAdmin(self), false},
|
||||||
|
// TODO: external apis exposed
|
||||||
|
rpc.API{"chequebook", chequebook.Version, chequebook.NewApi(self.config.Swap.Chequebook), true},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Swarm) Api() *api.Api {
|
func (self *Swarm) Api() *api.Api {
|
||||||
return self.api
|
return self.api
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend interface implemented by eth or JSON-IPC client
|
//
|
||||||
func (self *Swarm) SetChequebook(backend chequebook.Backend) (err error) {
|
func (self *Swarm) SetChequebook() (err error) {
|
||||||
done, err := self.config.Swap.SetChequebook(self.config.Path, backend)
|
done, err := self.config.Swap.SetChequebook(self.config.Path, self.backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
ok := <-done
|
ok := <-done
|
||||||
if ok {
|
if ok {
|
||||||
glog.V(logger.Info).Infof("[BZZ] Swarm: new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract)
|
glog.V(logger.Info).Infof("[BZZ] Swarm: new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex())
|
||||||
self.config.Save()
|
self.config.Save()
|
||||||
self.hive.DropAll()
|
self.hive.DropAll()
|
||||||
}
|
}
|
||||||
|
|
@ -223,9 +292,19 @@ func NewLocalSwarm(datadir, port string) (self *Swarm, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
self = &Swarm{
|
self = &Swarm{
|
||||||
api: api.NewApi(dpa, nil, config),
|
api: api.NewApi(dpa, nil),
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serialisable info about swarm
|
||||||
|
type Info struct {
|
||||||
|
*api.Config
|
||||||
|
*chequebook.Params
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Info) Info() *Info {
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
|
||||||
33
swarm/test/connections/00.sh
Normal file
33
swarm/test/connections/00.sh
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
dir=`dirname $0`
|
||||||
|
source $dir/../../cmd/swarm/test.sh
|
||||||
|
|
||||||
|
swarm init 4
|
||||||
|
echo "expect each node to have 3 peers"
|
||||||
|
cmd="'net.peerCount'"
|
||||||
|
sleep 5
|
||||||
|
swarm attach 00 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
|
||||||
|
swarm attach 01 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
|
||||||
|
swarm attach 02 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
|
||||||
|
swarm attach 03 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
|
||||||
|
|
||||||
|
swarm stop all
|
||||||
|
|
||||||
|
echo "after static nodes is deleted, connections are recovered from kaddb in bzz-peers.json"
|
||||||
|
# echo rm -rf $DATA_ROOT/enodes\*
|
||||||
|
# echo rm -rf $DATA_ROOT/data/\*/static-nodes.json
|
||||||
|
rm -rf $DATA_ROOT/enodes*
|
||||||
|
rm -rf $DATA_ROOT/data/*/static-nodes.json
|
||||||
|
|
||||||
|
swarm cluster 4
|
||||||
|
echo "expect each node to have 3 peers"
|
||||||
|
cmd="'net.peerCount'"
|
||||||
|
sleep 10
|
||||||
|
swarm attach 00 --exec "$cmd" |tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
|
||||||
|
swarm attach 01 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
|
||||||
|
swarm attach 02 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
|
||||||
|
swarm attach 03 --exec "$cmd"|tail -n1|grep -ql 3&& echo "PASS"||echo "FAIL"
|
||||||
|
|
||||||
|
swarm stop all
|
||||||
|
|
||||||
29
swarm/test/swap/00.sh
Normal file
29
swarm/test/swap/00.sh
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
echo "TEST swap/00:"
|
||||||
|
echo " two nodes that do not sync and do not have any funds"
|
||||||
|
echo " cannot retrieve content from each other"
|
||||||
|
|
||||||
|
dir=`dirname $0`
|
||||||
|
source $dir/../../cmd/swarm/test.sh
|
||||||
|
|
||||||
|
FILE_00=/tmp/1K.0
|
||||||
|
randomfile 1 > $FILE_00
|
||||||
|
ls -l $FILE_00
|
||||||
|
mininginterval=50
|
||||||
|
key=/tmp/key
|
||||||
|
|
||||||
|
swarm init 2 --bzznosync
|
||||||
|
sleep $wait
|
||||||
|
swarm up 00 $FILE_00|tail -n1 > $key
|
||||||
|
swarm needs 00 $key $FILE_00
|
||||||
|
echo -n "node 01 cannot download file 0: "
|
||||||
|
swarm needs 01 $key $FILE_00 | tail -1| grep -ql "PASS" && echo "FAIL" || echo "PASS"
|
||||||
|
|
||||||
|
FILE_01=/tmp/1K.1
|
||||||
|
randomfile 1 > $FILE_01
|
||||||
|
swarm up 01 $FILE_01|tail -1 > $key
|
||||||
|
swarm needs 01 $key $FILE_01
|
||||||
|
echo -n "node 00 cannot download file 1: "
|
||||||
|
swarm needs 00 $key $FILE_01 | tail -1| grep -ql "PASS" && echo "FAIL" || echo "PASS"
|
||||||
|
|
||||||
|
swarm stop all
|
||||||
43
swarm/test/swap/01.sh
Normal file
43
swarm/test/swap/01.sh
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
echo "TEST swap/01:"
|
||||||
|
echo " two nodes that do not sync but have enough funds"
|
||||||
|
echo " can retrieve content from each other"
|
||||||
|
|
||||||
|
dir=`dirname $0`
|
||||||
|
source $dir/../../cmd/swarm/test.sh
|
||||||
|
|
||||||
|
file=/tmp/test.file
|
||||||
|
mininginterval=120
|
||||||
|
key=/tmp/key
|
||||||
|
logargs="--verbosity=0 --vmodule='swarm/*=6'"
|
||||||
|
# logargs='--verbosity=6'
|
||||||
|
|
||||||
|
|
||||||
|
swarm init 2 --mine --bzznosync $logargs
|
||||||
|
|
||||||
|
echo "Mining some ether..."
|
||||||
|
sleep $mininginterval
|
||||||
|
|
||||||
|
randomfile 10 > $file
|
||||||
|
swarm up 00 $file|tail -n1 > $key
|
||||||
|
swarm needs 01 $key $file
|
||||||
|
swarm info 01
|
||||||
|
|
||||||
|
randomfile 200000 > $file
|
||||||
|
swarm up 00 $file |tail -n1 > $key
|
||||||
|
swarm needs 01 $key $file
|
||||||
|
|
||||||
|
randomfile 10 > $file
|
||||||
|
swarm up 00 $file|tail -n1 > $key
|
||||||
|
swarm needs 01 $key $file | tail -1| grep -ql "PASS" && echo "FAIL" || echo "PASS <3"
|
||||||
|
|
||||||
|
swarm status 00
|
||||||
|
swarm status 01
|
||||||
|
|
||||||
|
randomfile 10 > $file
|
||||||
|
swarm up 01 $file|tail -n1 > $key
|
||||||
|
swarm needs 00 $key $file | tail -1| grep -ql "PASS" && echo "FAIL" || echo "PASS <3"
|
||||||
|
|
||||||
|
swarm status 00
|
||||||
|
swarm status 01
|
||||||
|
|
||||||
|
swarm stop all
|
||||||
|
|
@ -1,83 +1,55 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
echo "TEST sync/00:"
|
||||||
|
echo " two nodes that sync (no swap and do not have any funds)"
|
||||||
|
echo " can be in sync content with each other"
|
||||||
|
|
||||||
function up { #file port
|
dir=`dirname $0`
|
||||||
echo "Upload file '$1' to node $2 on port 85$2" 1>&2
|
source $dir/../../cmd/swarm/test.sh
|
||||||
key=`bash swarm/cmd/bzzup.sh $1 85$2`
|
|
||||||
echo -n $key
|
|
||||||
}
|
|
||||||
|
|
||||||
function down { #key port
|
mkdir -p /tmp/swarm-test-files
|
||||||
echo "Download hash '$1' from node $2 on port 85$2"
|
FILE_00=/tmp/swarm-test-files/00
|
||||||
wget -O- http://localhost:85$2/$1 > /dev/null && echo "got it" || echo "not found"
|
FILE_01=/tmp/swarm-test-files/01
|
||||||
}
|
FILE_02=/tmp/swarm-test-files/02
|
||||||
|
FILE_03=/tmp/swarm-test-files/03
|
||||||
|
FILE_04=/tmp/swarm-test-files/04
|
||||||
|
|
||||||
function clean { #index
|
for f in $FILE_00 $FILE_01 $FILE_02 $FILE_03 $FILE_04; do
|
||||||
echo "Clean up for $1"
|
randomfile 20 > $f
|
||||||
rm -rf ~/tmp/sync/$1/{bzz/*/chunks,bzz/*/requests/,bzz/*/bzz-peers.json,chaindata,nodes}
|
done
|
||||||
}
|
# options="--verbosity=0 --vmodule=swarm/network/*=6,common/chequebook/*=6,common/swap/*=6,common/kademlia/*=5"
|
||||||
|
|
||||||
function gethup { #index account
|
key=/tmp/key
|
||||||
cp geth geth$1
|
swarm init 2 $options
|
||||||
echo "start node $1"
|
swarm info 00
|
||||||
echo "./geth$1 --datadir ~/tmp/sync/$1 --bzzaccount $2 --unlock 0 --password <(echo bzz) --networkid 323 --port 303$1 --vmodule netstore=6,depo=6,forwarding=6,hive=5,dpa=6,dpa=6,http=6,syncb=6,syncer=6,protocol=6,swap=6,chequebook=6 --shh=false --nodiscover --maxpeers 20 --dev --vmdebug=false --verbosity 4 2> sync$1.log &"
|
swarm info 01
|
||||||
./geth$1 --datadir ~/tmp/sync/$1 --bzzaccount "$2" --unlock 0 --password <(echo bzz) --networkid 323 --port 303$1 --vmodule netstore=6,depo=6,forwarding=6,hive=5,dpa=6,dpa=6,http=6,syncb=6,syncer=6,protocol=6,swap=6,chequebook=6 --shh=false --nodiscover --maxpeers 20 --dev --vmdebug=false --verbosity 4 2> sync$1.log
|
swarm up 00 $FILE_00|tail -n1 > $key
|
||||||
}
|
swarm needs 00 $key $FILE_00
|
||||||
|
# sleep $wait
|
||||||
|
swarm needs 01 $key $FILE_00
|
||||||
|
swarm stop 01
|
||||||
|
|
||||||
function gethdown { #index
|
# exit 1;
|
||||||
killall -INT geth$1
|
|
||||||
}
|
|
||||||
|
|
||||||
wait=5
|
swarm up 00 $FILE_01|tail -n1 > $key
|
||||||
gethdown 00
|
swarm needs 00 $key $FILE_01
|
||||||
gethdown 01
|
swarm start 01 $options
|
||||||
|
swarm needs 01 $key $FILE_01
|
||||||
|
|
||||||
# ./geth --datadir ~/tmp/sync/00 --password <(echo bzz) account new
|
swarm up 00 $FILE_02|tail -n1 > $key
|
||||||
BZZKEY00=5c6332e46a095feb9da1ed9803af2fa425f96aa6
|
swarm needs 00 $key $FILE_02
|
||||||
BZZKEY01=7dafa7436cba4b5b94a0452557b20b01d23bdc05
|
swarm needs 01 $key $FILE_02
|
||||||
echo "Fresh start, wipe datadir"
|
|
||||||
clean 00
|
|
||||||
clean 01
|
|
||||||
|
|
||||||
gethup 00 $BZZKEY00 &
|
swarm up 01 $FILE_03|tail -n1 > $key
|
||||||
sleep 2
|
swarm needs 01 $key $FILE_03
|
||||||
echo "beyond\n"
|
swarm needs 00 $key $FILE_03
|
||||||
key=$(up COPYING.LESSER 00)
|
|
||||||
echo "beyond\n"
|
|
||||||
down $key 00
|
|
||||||
|
|
||||||
echo "beyond\n"
|
swarm stop 00
|
||||||
gethup 01 $BZZKEY01 &
|
swarm up 01 $FILE_04|tail -n1 > $key
|
||||||
sleep $wait
|
swarm needs 01 $key $FILE_04
|
||||||
down $key 01
|
swarm start 00 #--bzznoswap
|
||||||
gethdown 01
|
swarm needs 00 $key $FILE_04
|
||||||
|
|
||||||
|
swarm stop all
|
||||||
key=$(up AUTHORS 00)
|
|
||||||
down $key 00
|
|
||||||
gethup 01 $BZZKEY01 &
|
|
||||||
sleep $wait
|
|
||||||
down $key 01
|
|
||||||
|
|
||||||
key=$(up COPYING 00)
|
|
||||||
down $key 00
|
|
||||||
down $key 01
|
|
||||||
|
|
||||||
key=$(up README.md 01)
|
|
||||||
down $key 01
|
|
||||||
sleep $wait
|
|
||||||
down $key 00
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
|
|
||||||
gethdown 00
|
|
||||||
key=$(up README.md 01)
|
|
||||||
down $key 01
|
|
||||||
gethup 00 $BZZKEY00 &
|
|
||||||
sleep $wait
|
|
||||||
down $key 00
|
|
||||||
|
|
||||||
gethdown 00
|
|
||||||
gethdown 01
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
24
swarm/test/syncing/01.sh
Normal file
24
swarm/test/syncing/01.sh
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/bash
|
||||||
|
echo "TEST sync/01:"
|
||||||
|
echo " two nodes that do not have any funds"
|
||||||
|
echo " can still sync content with each other"
|
||||||
|
|
||||||
|
dir=`dirname $0`
|
||||||
|
source $dir/../../cmd/swarm/test.sh
|
||||||
|
key=/tmp/key
|
||||||
|
|
||||||
|
long=/tmp/10M
|
||||||
|
randomfile 10000 > $long
|
||||||
|
ls -l $long
|
||||||
|
|
||||||
|
|
||||||
|
swarm init 2
|
||||||
|
sleep $wait
|
||||||
|
swarm up 00 $long |tail -n1 > $key
|
||||||
|
sleep $wait
|
||||||
|
swarm stop 01
|
||||||
|
|
||||||
|
swarm start 01
|
||||||
|
swarm needs 01 $key $long
|
||||||
|
|
||||||
|
swarm stop all
|
||||||
30
swarm/test/syncing/02.sh
Normal file
30
swarm/test/syncing/02.sh
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "TEST sync/02:"
|
||||||
|
echo " two nodes that sync (no swap and do not have any funds)"
|
||||||
|
echo " can sync content with each other even with intermittent network connection"
|
||||||
|
|
||||||
|
dir=`dirname $0`
|
||||||
|
source $dir/../../cmd/swarm/test.sh
|
||||||
|
|
||||||
|
long=/tmp/10M
|
||||||
|
key=/tmp/key
|
||||||
|
randomfile 10000 > $long
|
||||||
|
ls -l $long
|
||||||
|
|
||||||
|
swarm init 2
|
||||||
|
sleep $wait
|
||||||
|
swarm up 00 $long |tail -n1 > $key &
|
||||||
|
sleep $wait
|
||||||
|
swarm attach 01 -exec "'bzz.blockNetworkRead(true)'"
|
||||||
|
sleep $wait
|
||||||
|
swarm attach 01 -exec "'bzz.blockNetworkRead(false)'"
|
||||||
|
sleep $wait
|
||||||
|
swarm attach 01 -exec "'bzz.blockNetworkRead(true)'"
|
||||||
|
sleep $wait
|
||||||
|
swarm stop 01
|
||||||
|
|
||||||
|
swarm start 01
|
||||||
|
swarm needs 01 $key $long
|
||||||
|
|
||||||
|
swarm stop all
|
||||||
Loading…
Reference in a new issue