mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 21:56:43 +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 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 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 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 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
f732cd391a
commit
03b9ae7cf6
80 changed files with 4030 additions and 4138 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
|
||||
}
|
||||
|
|
@ -148,8 +148,8 @@ func (am *Manager) Sign(addr common.Address, hash []byte) (signature []byte, err
|
|||
}
|
||||
|
||||
func (am *Manager) GetUnlocked(addr common.Address) (prvkey *ecdsa.PrivateKey, err error) {
|
||||
am.mutex.RLock()
|
||||
defer am.mutex.RUnlock()
|
||||
am.mu.RLock()
|
||||
defer am.mu.RUnlock()
|
||||
unlockedKey, found := am.unlocked[addr]
|
||||
if !found {
|
||||
return nil, ErrLocked
|
||||
|
|
|
|||
|
|
@ -21,11 +21,8 @@ import (
|
|||
"io/ioutil"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -173,94 +170,10 @@ func accountList(ctx *cli.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// tries unlocking the specified account a few times.
|
||||
func unlockAccount(ctx *cli.Context, accman *accounts.Manager, address string, i int, passwords []string) (accounts.Account, string) {
|
||||
account, err := utils.MakeAddress(accman, address)
|
||||
if err != nil {
|
||||
utils.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
|
||||
utils.Fatalf("Failed to unlock account %s (%v)", address, err)
|
||||
return accounts.Account{}, ""
|
||||
}
|
||||
|
||||
// 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
|
||||
if prompt != "" {
|
||||
fmt.Println(prompt)
|
||||
}
|
||||
password, err := utils.Stdin.PasswordPrompt("Passphrase: ")
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to read passphrase: %v", err)
|
||||
}
|
||||
if confirmation {
|
||||
confirm, err := utils.Stdin.PasswordPrompt("Repeat passphrase: ")
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to read passphrase confirmation: %v", err)
|
||||
}
|
||||
if password != confirm {
|
||||
utils.Fatalf("Passphrases do not match")
|
||||
}
|
||||
}
|
||||
return password
|
||||
}
|
||||
|
||||
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 {
|
||||
utils.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
|
||||
}
|
||||
|
||||
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
||||
func accountCreate(ctx *cli.Context) {
|
||||
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)
|
||||
if err != nil {
|
||||
|
|
@ -277,8 +190,8 @@ func accountUpdate(ctx *cli.Context) {
|
|||
}
|
||||
accman := utils.MakeAccountManager(ctx)
|
||||
|
||||
account, oldPassword := unlockAccount(ctx, accman, ctx.Args().First(), 0, nil)
|
||||
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
|
||||
account, oldPassword := utils.UnlockAccount(accman, ctx.Args().First(), 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 {
|
||||
utils.Fatalf("Could not update the account: %v", err)
|
||||
}
|
||||
|
|
@ -295,7 +208,7 @@ func importWallet(ctx *cli.Context) {
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
|
|
@ -314,7 +227,7 @@ func accountImport(ctx *cli.Context) {
|
|||
utils.Fatalf("keyfile must be given as argument")
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the account: %v", err)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import (
|
|||
"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/registrar"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/internal/web3ext"
|
||||
re "github.com/ethereum/go-ethereum/jsre"
|
||||
|
|
@ -217,8 +216,6 @@ func (js *jsre) apiBindings() error {
|
|||
utils.Fatalf("Error setting namespaces: %v", err)
|
||||
}
|
||||
|
||||
js.re.Run(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
|
||||
|
||||
// overrule some of the methods that require password as input and ask for it interactively
|
||||
p, err := js.re.Get("personal")
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
"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/compiler"
|
||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||
|
|
@ -43,18 +42,17 @@ import (
|
|||
const (
|
||||
testSolcPath = ""
|
||||
solcVersion = "0.9.23"
|
||||
|
||||
testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674"
|
||||
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
testBalance = "10000000000000000000"
|
||||
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||
testBalance = "10000000000000000000"
|
||||
// of empty string
|
||||
testHash = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
||||
)
|
||||
|
||||
var (
|
||||
versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
|
||||
testNodeKey = crypto.ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f"))
|
||||
testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
|
||||
versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
|
||||
testNodeKey, _ = crypto.HexToECDSA("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")
|
||||
testAccount, _ = crypto.HexToECDSA("e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674")
|
||||
testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
|
||||
)
|
||||
|
||||
type testjethre struct {
|
||||
|
|
@ -63,17 +61,6 @@ type testjethre struct {
|
|||
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
|
||||
//func (self *testjethre) ConfirmTransaction(tx string) bool {
|
||||
// var ethereum *eth.Ethereum
|
||||
|
|
@ -95,18 +82,19 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
|
|||
t.Fatal(err)
|
||||
}
|
||||
// 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 {
|
||||
t.Fatalf("failed to create node: %v", err)
|
||||
}
|
||||
// Initialize and register the Ethereum protocol
|
||||
keystore := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
|
||||
accman := accounts.NewManager(keystore)
|
||||
|
||||
accman := accounts.NewPlaintextManager(filepath.Join(tmp, "keystore"))
|
||||
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{
|
||||
ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)},
|
||||
TestGenesisState: db,
|
||||
AccountManager: accman,
|
||||
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)
|
||||
}
|
||||
// Initialize all the keys for testing
|
||||
keyb, err := crypto.HexToECDSA(testKey)
|
||||
a, err := accman.ImportECDSA(testAccount, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key := crypto.NewKeyFromECDSA(keyb)
|
||||
if err := keystore.StoreKey(key, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := accman.Unlock(key.Address, ""); err != nil {
|
||||
if err := accman.Unlock(a, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 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)
|
||||
|
||||
assetPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
|
||||
//client := comms.NewInProcClient(codec.JSON)
|
||||
client := utils.NewInProcRPCClient(stack)
|
||||
client, err := stack.Attach()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to attach to node: %v", err)
|
||||
}
|
||||
tf := &testjethre{client: ethereum.HTTPClient()}
|
||||
repl := newJSRE(stack, assetPath, "", client, false)
|
||||
tf.jsre = repl
|
||||
|
|
@ -152,9 +138,6 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod
|
|||
func TestNodeInfo(t *testing.T) {
|
||||
t.Skip("broken after p2p update")
|
||||
tmp, repl, ethereum := testJEthRE(t)
|
||||
if err := ethereum.Start(); err != nil {
|
||||
t.Fatalf("error starting ethereum: %v", err)
|
||||
}
|
||||
defer ethereum.Stop()
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
|
|
@ -398,7 +381,7 @@ multiply7 = Multiply7.at(contractaddress);
|
|||
if sol != nil && solcVersion != sol.Version() {
|
||||
modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`))
|
||||
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 {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
// 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.
|
||||
// (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
|
||||
|
|
@ -216,9 +216,6 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
|
|||
utils.ExecFlag,
|
||||
utils.PreLoadJSFlag,
|
||||
utils.WhisperEnabledFlag,
|
||||
utils.SwarmConfigPathFlag,
|
||||
utils.SwarmAccountAddrFlag,
|
||||
utils.ChequebookAddrFlag,
|
||||
utils.DevModeFlag,
|
||||
utils.TestNetFlag,
|
||||
utils.VMForceJitFlag,
|
||||
|
|
@ -236,6 +233,13 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
|
|||
utils.GpobaseStepUpFlag,
|
||||
utils.GpobaseCorrectionFactorFlag,
|
||||
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...)
|
||||
|
||||
|
|
@ -440,16 +444,6 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
|||
if err := stack.Service(ðereum); err != nil {
|
||||
utils.Fatalf("ethereum service not running: %v", err)
|
||||
}
|
||||
accman := ethereum.AccountManager()
|
||||
passwords := utils.MakePasswordList(ctx)
|
||||
|
||||
accounts := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
|
||||
for i, account := range accounts {
|
||||
if trimmed := strings.TrimSpace(account); trimmed != "" {
|
||||
unlockAccount(ctx, accman, trimmed, i, passwords)
|
||||
}
|
||||
}
|
||||
// Start auxiliary services if enabled
|
||||
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
|
||||
if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil {
|
||||
utils.Fatalf("Failed to start mining: %v", err)
|
||||
|
|
@ -457,78 +451,6 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
|||
}
|
||||
}
|
||||
|
||||
func accountList(ctx *cli.Context) {
|
||||
accman := utils.MakeAccountManager(ctx)
|
||||
accts, err := accman.Accounts()
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not list accounts: %v", err)
|
||||
}
|
||||
for i, acct := range accts {
|
||||
fmt.Printf("Account #%d: %x\n", i, acct)
|
||||
}
|
||||
}
|
||||
|
||||
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
||||
func accountCreate(ctx *cli.Context) {
|
||||
accman := utils.MakeAccountManager(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)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to create account: %v", err)
|
||||
}
|
||||
fmt.Printf("Address: %x\n", account)
|
||||
}
|
||||
|
||||
// accountUpdate transitions an account from a previous format to the current
|
||||
// one, also providing the possibility to change the pass-phrase.
|
||||
func accountUpdate(ctx *cli.Context) {
|
||||
if len(ctx.Args()) == 0 {
|
||||
utils.Fatalf("No accounts specified to update")
|
||||
}
|
||||
accman := utils.MakeAccountManager(ctx)
|
||||
|
||||
account, oldPassword := utils.UnlockAccount(ctx, accman, ctx.Args().First(), 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 {
|
||||
utils.Fatalf("Could not update the account: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func importWallet(ctx *cli.Context) {
|
||||
keyfile := ctx.Args().First()
|
||||
if len(keyfile) == 0 {
|
||||
utils.Fatalf("keyfile must be given as argument")
|
||||
}
|
||||
keyJson, err := ioutil.ReadFile(keyfile)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not read wallet file: %v", err)
|
||||
}
|
||||
|
||||
accman := utils.MakeAccountManager(ctx)
|
||||
passphrase := utils.GetPassPhrase("", false, 0, utils.MakePasswordList(ctx))
|
||||
|
||||
acct, err := accman.ImportPreSaleKey(keyJson, passphrase)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the account: %v", err)
|
||||
}
|
||||
fmt.Printf("Address: %x\n", acct)
|
||||
}
|
||||
|
||||
func accountImport(ctx *cli.Context) {
|
||||
keyfile := ctx.Args().First()
|
||||
if len(keyfile) == 0 {
|
||||
utils.Fatalf("keyfile must be given as argument")
|
||||
}
|
||||
accman := utils.MakeAccountManager(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.Import(keyfile, passphrase)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the account: %v", err)
|
||||
}
|
||||
fmt.Printf("Address: %x\n", acct)
|
||||
}
|
||||
|
||||
func makedag(ctx *cli.Context) {
|
||||
args := ctx.Args()
|
||||
wrongArgs := func() {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"os/signal"
|
||||
"regexp"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"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)
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,12 +50,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/release"
|
||||
"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"
|
||||
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
|
|
@ -357,22 +351,6 @@ var (
|
|||
Name: "shh",
|
||||
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
|
||||
JSpathFlag = cli.StringFlag{
|
||||
Name: "jspath",
|
||||
|
|
@ -416,6 +394,32 @@ var (
|
|||
Usage: "Suggested gas price base correction factor (%)",
|
||||
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
|
||||
|
|
@ -664,53 +668,6 @@ func MakePasswordList(ctx *cli.Context) []string {
|
|||
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
|
||||
// assembles the P2P protocol stack.
|
||||
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 {
|
||||
Fatalf("The %v flags are mutually exclusive", netFlags)
|
||||
}
|
||||
// Configure the node's service container
|
||||
datadir := MustMakeDataDir(ctx)
|
||||
netprv := MakeNodeKey(ctx)
|
||||
// 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), ",")
|
||||
for i, account := range accounts {
|
||||
if trimmed := strings.TrimSpace(account); trimmed != "" {
|
||||
UnlockAccount(ctx, accman, trimmed, i, passwords)
|
||||
UnlockAccount(accman, trimmed, i, passwords)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -844,15 +802,11 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
|||
if err != nil {
|
||||
Fatalf("Failed to create the protocol stack: %v", err)
|
||||
}
|
||||
|
||||
// eth.Ethereum: ethereum
|
||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
return eth.New(ctx, ethConf)
|
||||
}); err != nil {
|
||||
Fatalf("Failed to register the Ethereum service: %v", err)
|
||||
}
|
||||
|
||||
// Whisper
|
||||
if shhEnable {
|
||||
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)
|
||||
|
|
@ -862,13 +816,14 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
|||
return release.NewReleaseService(ctx, relconf)
|
||||
}); err != nil {
|
||||
Fatalf("Failed to register the Geth release oracle service: %v", err)
|
||||
}
|
||||
|
||||
// bzz. Swarm
|
||||
var bzzconfig *bzzapi.Config
|
||||
hexaddr := ctx.GlobalString(SwarmAccountAddrFlag.Name)
|
||||
if hexaddr != "" {
|
||||
swarmaccount := common.HexToAddress(hexaddr)
|
||||
if !accman.HasAccount(swarmaccount) {
|
||||
if !accman.HasAddress(swarmaccount) {
|
||||
Fatalf("swarm account '%v' does not exist: %v", hexaddr, err)
|
||||
}
|
||||
prvkey, err := accman.GetUnlocked(swarmaccount)
|
||||
|
|
@ -884,13 +839,19 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
|||
if err != nil {
|
||||
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) {
|
||||
return swarm.NewSwarm(ctx, bzzconfig, swap)
|
||||
return swarm.NewSwarm(ctx, bzzconfig, swapEnabled, syncEnabled)
|
||||
}); err != nil {
|
||||
Fatalf("Failed to register the Swarm service: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return stack
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,3 +96,33 @@ func (r *userInputReader) ConfirmPrompt(prompt string) (bool, error) {
|
|||
}
|
||||
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 {
|
||||
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)
|
||||
defer zeroBytes(seckey)
|
||||
// defer zeroBytes(seckey)
|
||||
sig, err = secp256k1.Sign(hash, seckey)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ func TestSign(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("Sign error: %s", err)
|
||||
}
|
||||
|
||||
recoveredPub, err := Ecrecover(msg, sig)
|
||||
if err != nil {
|
||||
t.Errorf("ECRecover error: %s", err)
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ import (
|
|||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/registrar"
|
||||
)
|
||||
|
||||
func TestReadBits(t *testing.T) {
|
||||
|
|
@ -39,34 +37,3 @@ func TestReadBits(t *testing.T) {
|
|||
check("0000000000012345000000000000000000000000000000000000FEFCF3F8F0")
|
||||
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/compiler"
|
||||
"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/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
|
@ -342,16 +341,12 @@ func (s *Ethereum) APIs() []rpc.API {
|
|||
}, {
|
||||
Namespace: "debug",
|
||||
Version: "1.0",
|
||||
Service: NewPrivateDebugAPI(s.chainConfig, s),
|
||||
Service: NewPrivateDebugAPI(s.ChainConfig(), s),
|
||||
}, {
|
||||
Namespace: "net",
|
||||
Version: "1.0",
|
||||
Service: s.netRPCService,
|
||||
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) 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) EventMux() *event.TypeMux { return s.eventMux }
|
||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||
|
|
|
|||
21
eth/bind.go
21
eth/bind.go
|
|
@ -21,6 +21,8 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"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/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
|
@ -37,6 +39,7 @@ type ContractBackend struct {
|
|||
eapi *PublicEthereumAPI // Wrapper around the Ethereum object to access metadata
|
||||
bcapi *PublicBlockChainAPI // Wrapper around the blockchain to access chain data
|
||||
txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data
|
||||
eth *Ethereum
|
||||
}
|
||||
|
||||
// NewContractBackend creates a new native contract backend using an existing
|
||||
|
|
@ -46,6 +49,7 @@ func NewContractBackend(eth *Ethereum) *ContractBackend {
|
|||
eapi: NewPublicEthereumAPI(eth),
|
||||
bcapi: NewPublicBlockChainAPI(eth.chainConfig, eth.blockchain, eth.miner, eth.chainDb, eth.gpo, eth.eventMux, eth.accountManager),
|
||||
txapi: NewPublicTransactionPoolAPI(eth),
|
||||
eth: eth,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,3 +112,20 @@ func (b *ContractBackend) SendTransaction(tx *types.Transaction) error {
|
|||
_, err := b.txapi.SendRawTransaction(common.ToHex(raw))
|
||||
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
|
||||
FastSync // Quickly download the headers, full sync only at the chain head
|
||||
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,181 @@
|
|||
package web3ext
|
||||
|
||||
var Modules = map[string]string{
|
||||
"txpool": TxPool_JS,
|
||||
"admin": Admin_JS,
|
||||
"personal": Personal_JS,
|
||||
"eth": Eth_JS,
|
||||
"miner": Miner_JS,
|
||||
"debug": Debug_JS,
|
||||
"net": Net_JS,
|
||||
"txpool": TxPool_JS,
|
||||
"admin": Admin_JS,
|
||||
"personal": Personal_JS,
|
||||
"eth": Eth_JS,
|
||||
"miner": Miner_JS,
|
||||
"debug": Debug_JS,
|
||||
"net": Net_JS,
|
||||
"bzz": Bzz_JS,
|
||||
"ens": ENS_JS,
|
||||
"chequebook": Chequebook_JS,
|
||||
}
|
||||
|
||||
const TxPool_JS = `
|
||||
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: '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:
|
||||
[
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
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',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
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:
|
||||
[
|
||||
|
|
@ -176,20 +340,6 @@ web3._extend({
|
|||
});
|
||||
`
|
||||
|
||||
const Personal_JS = `
|
||||
web3._extend({
|
||||
property: 'personal',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'importRawKey',
|
||||
call: 'personal_importRawKey',
|
||||
params: 2
|
||||
})
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const Eth_JS = `
|
||||
web3._extend({
|
||||
property: 'eth',
|
||||
|
|
|
|||
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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
"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/glog"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -27,68 +19,91 @@ var (
|
|||
domainAndVersion = regexp.MustCompile("[@:;,]+")
|
||||
)
|
||||
|
||||
type Resolver interface {
|
||||
Resolve(string) (storage.Key, error)
|
||||
}
|
||||
|
||||
/*
|
||||
Api implements webserver/file system related content storage and retrieval
|
||||
on top of the dpa
|
||||
it is the public interface of the dpa which is included in the ethereum stack
|
||||
*/
|
||||
type Api struct {
|
||||
dpa *storage.DPA
|
||||
registrar registrar.VersionedRegistrar
|
||||
conf *Config
|
||||
dpa *storage.DPA
|
||||
dns Resolver
|
||||
}
|
||||
|
||||
//the api constructor initialises
|
||||
func NewApi(dpa *storage.DPA, registrar registrar.VersionedRegistrar, conf *Config) (self *Api) {
|
||||
return &Api{dpa, registrar, conf}
|
||||
}
|
||||
|
||||
// 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
|
||||
func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) {
|
||||
self = &Api{
|
||||
dpa: dpa,
|
||||
dns: dns,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Put provides singleton manifest creation and optional name registration
|
||||
// on top of dpa store
|
||||
// DPA reader API
|
||||
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.Debug).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.Debug).Infof("[BZZ] DNS error : %v", err)
|
||||
}
|
||||
glog.V(logger.Debug).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) {
|
||||
sr := io.NewSectionReader(strings.NewReader(content), 0, int64(len(content)))
|
||||
wg := &sync.WaitGroup{}
|
||||
|
|
@ -106,8 +121,36 @@ func (self *Api) Put(content, contentType string) (string, error) {
|
|||
return key.String(), nil
|
||||
}
|
||||
|
||||
func (self *Api) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) {
|
||||
root := common.Hex2Bytes(rootHash)
|
||||
// Get uses iterative manifest retrieval and prefix matching
|
||||
// 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.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
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -130,327 +173,3 @@ func (self *Api) Modify(rootHash, path, contentHash, contentType string) (newRoo
|
|||
}
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
// "bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
)
|
||||
|
||||
//TODO: add tests for resolver/registrar
|
||||
// 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) {
|
||||
func testApi(t *testing.T, f func(*Api)) {
|
||||
datadir, err := ioutil.TempDir("", "bzz-test")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
t.Fatalf("unable to create temp dir: %v", err)
|
||||
}
|
||||
os.RemoveAll(datadir)
|
||||
defer os.RemoveAll(datadir)
|
||||
dpa, err := storage.NewLocalDPA(datadir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
prvkey, _ := crypto.GenerateKey()
|
||||
api := NewApi(dpa, nil)
|
||||
dpa.Start()
|
||||
f(api)
|
||||
dpa.Stop()
|
||||
}
|
||||
|
||||
config, err := NewConfig(datadir, common.Address{}, prvkey)
|
||||
if err != nil {
|
||||
return
|
||||
type testResponse struct {
|
||||
reader storage.SectionReader
|
||||
*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)
|
||||
api.dpa.Start()
|
||||
if resp.Status != exp.Status {
|
||||
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) {
|
||||
api, err := testApi()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
defer api.dpa.Stop()
|
||||
expContent := "hello"
|
||||
expMimeType := "text/plain"
|
||||
expStatus := 0
|
||||
expSize := len(expContent)
|
||||
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)
|
||||
testApi(t, func(api *Api) {
|
||||
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)
|
||||
}
|
||||
resp := testGet(t, api, bzzhash)
|
||||
checkResponse(t, resp, exp)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ func TestConfigWriteRead(t *testing.T) {
|
|||
t.Fatalf("default config file cannot be read: %v", err)
|
||||
}
|
||||
exp := strings.Replace(defaultConfig, "TMPDIR", tmp, -1)
|
||||
exp = strings.Replace(exp, "\\", "\\\\", -1)
|
||||
|
||||
if string(data) != exp {
|
||||
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 }
|
||||
258
swarm/api/filesystem.go
Normal file
258
swarm/api/filesystem.go
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
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
|
||||
//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.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.Debug).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 (
|
||||
"io/ioutil"
|
||||
|
|
@ -22,7 +22,7 @@ func TestRoundTripper(t *testing.T) {
|
|||
})
|
||||
go http.ListenAndServe(":8600", serveMux)
|
||||
|
||||
rt := &RoundTripper{"8600"}
|
||||
rt := &RoundTripper{Port: "8600"}
|
||||
client := httpclient.New("/")
|
||||
client.RegisterProtocol("bzz", rt)
|
||||
|
||||
|
|
@ -43,8 +43,8 @@ func TestRoundTripper(t *testing.T) {
|
|||
t.Errorf("expected no error, got %v", err)
|
||||
return
|
||||
}
|
||||
if string(content) != "/test.com/path" {
|
||||
t.Errorf("incorrect response from http server: expected '%v', got '%v'", "/test.com/path", string(content))
|
||||
if string(content) != "/HTTP/1.1:/test.com/path" {
|
||||
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
|
||||
*/
|
||||
package api
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"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/api"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -21,8 +22,8 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
bzzPrefix = regexp.MustCompile("^/+bzz:/+")
|
||||
rawUrl = regexp.MustCompile("^/+raw/*")
|
||||
// accepted protocols: bzz (traditional), bzzi (immutable) and bzzr (raw)
|
||||
bzzPrefix = regexp.MustCompile("^/+bzz[ir]?:/+")
|
||||
trailingSlashes = regexp.MustCompile("/+$")
|
||||
// forever = func() time.Time { return time.Unix(0, 0) }
|
||||
forever = time.Now
|
||||
|
|
@ -41,7 +42,7 @@ type sequentialReader struct {
|
|||
// https://github.com/atom/electron/blob/master/docs/api/protocol.md
|
||||
|
||||
// starts up http server
|
||||
func StartHttpServer(api *Api, port string) {
|
||||
func StartHttpServer(api *api.Api, port string) {
|
||||
serveMux := http.NewServeMux()
|
||||
serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
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)
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||
func handler(w http.ResponseWriter, r *http.Request, a *api.Api) {
|
||||
requestURL := r.URL
|
||||
// This is wrong
|
||||
// if requestURL.Host == "" {
|
||||
|
|
@ -61,24 +62,41 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
|||
// 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
|
||||
var raw bool
|
||||
var raw, nameresolver bool
|
||||
var proto string
|
||||
|
||||
// HTTP-based URL protocol handler
|
||||
uri = bzzPrefix.ReplaceAllString(uri, "")
|
||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: BZZ request URI: '%s'", uri)
|
||||
|
||||
path := rawUrl.ReplaceAllStringFunc(uri, func(string) string {
|
||||
raw = true
|
||||
path := bzzPrefix.ReplaceAllStringFunc(uri, func(p string) string {
|
||||
proto = p
|
||||
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 {
|
||||
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,
|
||||
ahead: make(map[int64]chan bool),
|
||||
}, 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)
|
||||
return
|
||||
} else {
|
||||
path = regularSlashes(path)
|
||||
path = api.RegularSlashes(path)
|
||||
mime := r.Header.Get("Content-Type")
|
||||
// TODO proper root hash separation
|
||||
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 {
|
||||
glog.V(logger.Debug).Infof("[BZZ] Swarm replaced manifest by '%s'", newKey)
|
||||
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)
|
||||
return
|
||||
} else {
|
||||
path = regularSlashes(path)
|
||||
path = api.RegularSlashes(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 {
|
||||
glog.V(logger.Debug).Infof("[BZZ] Swarm replaced manifest by '%s'", newKey)
|
||||
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, "")
|
||||
if raw {
|
||||
// resolving host
|
||||
key, err := api.Resolve(path)
|
||||
key, err := a.Resolve(path, nameresolver)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("[BZZ] Swarm: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
|
|
@ -146,7 +164,7 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
|||
}
|
||||
|
||||
// retrieving content
|
||||
reader := api.dpa.Retrieve(key)
|
||||
reader := a.Retrieve(key)
|
||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: Reading %d bytes.", reader.Size())
|
||||
|
||||
// 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)
|
||||
|
||||
// call to api.getPath on uri
|
||||
reader, mimeType, status, err := api.getPath(path)
|
||||
reader, mimeType, status, err := a.Get(path, nameresolver)
|
||||
if err != nil {
|
||||
if _, ok := err.(errResolve); ok {
|
||||
if _, ok := err.(api.ErrResolve); ok {
|
||||
glog.V(logger.Debug).Infof("[BZZ] Swarm: %v", err)
|
||||
status = http.StatusBadRequest
|
||||
} else {
|
||||
|
|
@ -145,7 +145,10 @@ func (self *manifestTrie) deleteEntry(path string) {
|
|||
|
||||
b := byte(path[0])
|
||||
entry := self.entries[b]
|
||||
if (entry != nil) && (entry.Path == path) {
|
||||
if entry == nil {
|
||||
return
|
||||
}
|
||||
if entry.Path == path {
|
||||
self.entries[b] = nil
|
||||
return
|
||||
}
|
||||
|
|
@ -290,7 +293,7 @@ func (self *manifestTrie) findPrefixOf(path string) (entry *manifestTrieEntry, p
|
|||
|
||||
// file system manifest always contains regularized paths
|
||||
// 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++ {
|
||||
if (path[i] != '/') || ((i > 0) && (path[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) {
|
||||
path := regularSlashes(spath)
|
||||
path := RegularSlashes(spath)
|
||||
var pos int
|
||||
entry, pos = self.findPrefixOf(path)
|
||||
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)
|
||||
}
|
||||
56
swarm/api/storage.go
Normal file
56
swarm/api/storage.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
// "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"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
glog.V(logger.Detail).Infof("body: %s", body[:size])
|
||||
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)
|
||||
})
|
||||
}
|
||||
28
swarm/api/testapi.go
Normal file
28
swarm/api/testapi.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
// "fmt"
|
||||
|
||||
"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)
|
||||
}
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
#! /bin/bash
|
||||
|
||||
INDEX='index.html'
|
||||
port="8500"
|
||||
proxy="http://localhost:8500"
|
||||
delimiter='{"entries":[{'
|
||||
|
||||
|
||||
if [[ ! -z "$2" ]]; then
|
||||
port="$2"
|
||||
proxy="$2"
|
||||
fi
|
||||
|
||||
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"`
|
||||
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
|
||||
|
||||
else
|
||||
|
|
@ -28,8 +30,11 @@ do
|
|||
name=`echo "$path" | cut -c3-`
|
||||
[ _`basename "$name"` = "_$INDEX" ] && name=`dirname "$name"`
|
||||
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"`
|
||||
if [ "$mime" = "text/plain" ]; then
|
||||
echo -n $path|grep -q '.css' && mime="text/css"
|
||||
fi
|
||||
if [ "_$name" = '_.' ]; then
|
||||
echo -n "\"hash\":\"$hash\",\"contentType\":\"$mime\""
|
||||
else
|
||||
|
|
@ -38,7 +43,7 @@ fi
|
|||
delimiter='},{'
|
||||
|
||||
done
|
||||
echo -n '}]}') | wget -q -O- --post-data=`cat` http://localhost:$port/raw
|
||||
echo -n '}]}') | wget -q -O- --post-data=`cat` $proxy/bzzr:/
|
||||
echo
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -288,7 +288,7 @@ function uploadFile(files, nr, uri) {
|
|||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function() { if (xhr.readyState === 4) {
|
||||
var i = xhr.responseText;
|
||||
window.location.replace("/" + i + "/");
|
||||
window.location.replace("/bzz:/" + i + "/");
|
||||
}};
|
||||
sendImgs(xhr, uri);
|
||||
}
|
||||
|
|
@ -327,9 +327,9 @@ function uploadFile(files, nr, uri) {
|
|||
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 + "/");
|
||||
uploadFile(files, nr + 1, "/bzz:/" + i + "/");
|
||||
}
|
||||
img.src = "/" + i + "/imgs/" + file.name;
|
||||
img.src = "/bzz:/" + i + "/imgs/" + file.name;
|
||||
return;
|
||||
}};
|
||||
xhr.open("PUT", uri + "imgs/" + file.name, true);
|
||||
|
|
@ -361,9 +361,9 @@ function deleteImg()
|
|||
var xhrd = new XMLHttpRequest();
|
||||
xhrd.onreadystatechange = function () { if (xhrd.readyState === 4) {
|
||||
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();
|
||||
}};
|
||||
|
||||
|
|
@ -378,7 +378,7 @@ function moveUpDown(off)
|
|||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function () { if (xhr.readyState === 4) {
|
||||
var i = xhr.responseText;
|
||||
window.location.replace("/" + i + "/#" + (eidx + off));
|
||||
window.location.replace("/bzz:/" + i + "/#" + (eidx + off));
|
||||
}};
|
||||
sendImgs(xhr, "");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,21 @@
|
|||
navigator.registerProtocolHandler("bzz",
|
||||
"http://localhost:8500/%s",
|
||||
"Swarm handler");
|
||||
navigator.registerProtocolHandler("bzzi",
|
||||
"http://localhost:8500/%s",
|
||||
"Swarm immutable handler");
|
||||
navigator.registerProtocolHandler("bzzr",
|
||||
"http://localhost:8500/%s",
|
||||
"Swarm raw handler");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import (
|
|||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
)
|
||||
|
||||
// Handler for storage/retrieval related protocol requests
|
||||
|
|
@ -51,6 +51,7 @@ func (self *Depo) HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// set peers state to persist
|
||||
p.syncState = req.State
|
||||
return nil
|
||||
}
|
||||
|
|
@ -124,7 +125,11 @@ func (self *Depo) HandleRetrieveRequestMsg(req *retrieveRequestMsgData, p *peer)
|
|||
req.from = p
|
||||
// swap - record credit for 1 request
|
||||
// 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)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import (
|
|||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
)
|
||||
|
||||
const requesterCount = 3
|
||||
|
|
@ -18,7 +18,6 @@ via the native bzz protocol
|
|||
which uses an MSB logarithmic distance-based semi-permanent Kademlia table for
|
||||
* recursive forwarding style routing for retrieval
|
||||
* smart syncronisation
|
||||
* TODO: beeline delivery, IPFS, IPΞS
|
||||
*/
|
||||
|
||||
type forwarder struct {
|
||||
|
|
@ -42,26 +41,30 @@ var searchTimeout = 3 * time.Second
|
|||
func (self *forwarder) Retrieve(chunk *storage.Chunk) {
|
||||
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))
|
||||
OUT:
|
||||
for _, p := range peers {
|
||||
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 _, recipient := range recipients {
|
||||
req := recipient.(*retrieveRequestMsgData)
|
||||
if req.from.Addr() == p.Addr() {
|
||||
break OUT
|
||||
continue OUT
|
||||
}
|
||||
}
|
||||
}
|
||||
if req != nil {
|
||||
if err := p.swap.Add(-1); err == nil {
|
||||
p.retrieve(req)
|
||||
break
|
||||
} else {
|
||||
glog.V(logger.Warn).Infof("[BZZ] forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err)
|
||||
}
|
||||
req := &retrieveRequestMsgData{
|
||||
Key: chunk.Key,
|
||||
Id: generateId(),
|
||||
}
|
||||
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)
|
||||
}
|
||||
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++
|
||||
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
|
||||
|
|
@ -104,7 +107,7 @@ func (self *forwarder) Deliver(chunk *storage.Chunk) {
|
|||
for id, r := range requesters {
|
||||
req = r.(*retrieveRequestMsgData)
|
||||
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)
|
||||
Deliver(req.from, msg, DeliverReq)
|
||||
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"
|
||||
|
||||
"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/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/swarm/network/kademlia"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
)
|
||||
|
||||
|
|
@ -32,13 +32,19 @@ type Hive struct {
|
|||
path string
|
||||
toggle chan bool
|
||||
more chan bool
|
||||
|
||||
// for testing only
|
||||
swapEnabled bool
|
||||
syncEnabled bool
|
||||
blockRead bool
|
||||
blockWrite bool
|
||||
}
|
||||
|
||||
const (
|
||||
callInterval = 10000000000
|
||||
bucketSize = 3
|
||||
maxProx = 10
|
||||
proxBinSize = 8
|
||||
callInterval = 3000000000
|
||||
// bucketSize = 3
|
||||
// maxProx = 8
|
||||
// proxBinSize = 4
|
||||
)
|
||||
|
||||
type HiveParams struct {
|
||||
|
|
@ -49,9 +55,9 @@ type HiveParams struct {
|
|||
|
||||
func NewHiveParams(path string) *HiveParams {
|
||||
kad := kademlia.NewKadParams()
|
||||
kad.BucketSize = bucketSize
|
||||
kad.MaxProx = maxProx
|
||||
kad.ProxBinSize = proxBinSize
|
||||
// kad.BucketSize = bucketSize
|
||||
// kad.MaxProx = maxProx
|
||||
// kad.ProxBinSize = proxBinSize
|
||||
|
||||
return &HiveParams{
|
||||
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)
|
||||
return &Hive{
|
||||
callInterval: params.CallInterval,
|
||||
kad: kad,
|
||||
addr: kad.Addr(),
|
||||
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
|
||||
func (self *Hive) Addr() kademlia.Address {
|
||||
return self.addr
|
||||
|
|
@ -122,7 +146,7 @@ func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPee
|
|||
} else {
|
||||
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
|
||||
|
|
@ -134,7 +158,7 @@ func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPee
|
|||
// wake state is toggled by writing to self.toggle
|
||||
// it restarts if the table becomes non-full again due to disconnections
|
||||
func (self *Hive) keepAlive() {
|
||||
var alarm <-chan time.Time
|
||||
alarm := time.NewTicker(time.Duration(self.callInterval)).C
|
||||
for {
|
||||
select {
|
||||
case <-alarm:
|
||||
|
|
@ -262,10 +286,14 @@ func (self *peer) LastActive() time.Time {
|
|||
func loadSync(record *kademlia.NodeRecord, node kademlia.Node) error {
|
||||
if p, ok := node.(*peer); ok {
|
||||
if record.Meta == nil {
|
||||
glog.V(logger.Debug).Infof("no sync state for node record %v", record)
|
||||
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.Debug).Infof("sync state for node record %v: %s -> %v", record, string(*(record.Meta)), state)
|
||||
p.syncState = state
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
bucketSize = 20
|
||||
maxProx = 255
|
||||
bucketSize = 3
|
||||
proxBinSize = 4
|
||||
maxProx = 8
|
||||
connRetryExp = 2
|
||||
)
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ type KadParams struct {
|
|||
func NewKadParams() *KadParams {
|
||||
return &KadParams{
|
||||
MaxProx: maxProx,
|
||||
ProxBinSize: bucketSize,
|
||||
ProxBinSize: proxBinSize,
|
||||
BucketSize: bucketSize,
|
||||
PurgeInterval: purgeInterval,
|
||||
InitialRetryInterval: initialRetryInterval,
|
||||
|
|
@ -72,6 +73,8 @@ func New(addr Address, params *KadParams) *Kademlia {
|
|||
}
|
||||
glog.V(logger.Info).Infof("[KΛÐ] base address %v", addr)
|
||||
|
||||
// ! temporary hack fixme:
|
||||
params.ProxBinSize = 4
|
||||
return &Kademlia{
|
||||
addr: addr,
|
||||
KadParams: params,
|
||||
|
|
@ -100,6 +103,9 @@ func (self *Kademlia) DBCount() int {
|
|||
// 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)
|
||||
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())
|
||||
record := self.db.findOrCreate(index, node.Addr(), node.Url())
|
||||
// callback on add node
|
||||
|
|
@ -116,25 +122,20 @@ func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error
|
|||
}
|
||||
record.connected = true
|
||||
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
|
||||
// insert in kademlia table of active nodes
|
||||
bucket := self.buckets[index]
|
||||
// if bucket is full insertion replaces the worst node
|
||||
// TODO: give priority to peers with active traffic
|
||||
if worst, pos := bucket.insert(node); worst != nil {
|
||||
glog.V(logger.Info).Infof("[KΛÐ]: replace node %v (%d) with node %v", worst, pos, node)
|
||||
glog.V(logger.Info).Infof("[KΛÐ]: replace node %v (%d) with node %v\n%v", worst, pos, node, self)
|
||||
return
|
||||
// no prox adjustment needed
|
||||
// do not change count
|
||||
} else {
|
||||
glog.V(logger.Info).Infof("[KΛÐ]: add node %v to table", node)
|
||||
self.count++
|
||||
self.adjustProxMore(index)
|
||||
}
|
||||
|
||||
glog.V(logger.Info).Infof("[KΛÐ]: add node %v to table", node)
|
||||
self.count++
|
||||
self.setProxLimit(index, false)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// is the entrypoint called when a node is taken offline
|
||||
|
|
@ -161,7 +162,8 @@ func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) {
|
|||
if len(bucket.nodes) < bucket.size {
|
||||
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()]
|
||||
// callback on remove
|
||||
|
|
@ -174,49 +176,40 @@ func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// proxLimit is dynamically adjusted so that 1) there is no
|
||||
// empty buckets in bin < proxLimit and 2) the sum of all items sare the maximum
|
||||
// possible but lower than ProxBinSize
|
||||
// adjust Prox (proxLimit and proxSize after an insertion of add nodes into bucket r)
|
||||
func (self *Kademlia) adjustProxMore(r int) {
|
||||
if r >= self.proxLimit {
|
||||
exLimit := self.proxLimit
|
||||
exSize := self.proxSize
|
||||
self.proxSize++
|
||||
|
||||
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)
|
||||
// proxLimit is dynamically adjusted so that
|
||||
// 1) there is no empty buckets in bin < proxLimit and
|
||||
// 2) the sum of all items sare the maximpossible but lower than ProxBinSize
|
||||
// adjust Prox (proxLimit and proxSize after an insertion/removal of nodes)
|
||||
// caller holds the lock
|
||||
func (self *Kademlia) setProxLimit(r int, off bool) {
|
||||
// glog.V(logger.Info).Infof("[KΛÐ]: adjust proxbin for (bin: %v, off: %v)", r, off)
|
||||
if r < self.proxLimit && len(self.buckets[r].nodes) > 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Kademlia) adjustProxLess(r int) {
|
||||
exLimit := self.proxLimit
|
||||
exSize := self.proxSize
|
||||
if r >= self.proxLimit {
|
||||
glog.V(logger.Detail).Infof("[KΛÐ]: set proxbin (size: %v, limit: %v, bin: %v, off: %v)", self.proxSize, self.proxLimit, r, off)
|
||||
if off {
|
||||
self.proxSize--
|
||||
}
|
||||
|
||||
if r < self.proxLimit && len(self.buckets[r].nodes) == 0 {
|
||||
for i := self.proxLimit - 1; i > r; i-- {
|
||||
self.proxSize += len(self.buckets[i].nodes)
|
||||
for (self.proxSize < self.ProxBinSize || r < self.proxLimit) &&
|
||||
self.proxLimit > 0 {
|
||||
//
|
||||
self.proxLimit--
|
||||
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
|
||||
} else if self.proxLimit > 0 && r >= self.proxLimit-1 {
|
||||
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
|
||||
glog.V(logger.Detail).Infof("%v", self)
|
||||
return
|
||||
}
|
||||
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 +244,7 @@ func (self *Kademlia) FindClosest(target Address, max int) []Node {
|
|||
r.push(bucket[i], limit)
|
||||
n++
|
||||
}
|
||||
if max == 0 && start <= index && (n > 0 || start == 0) ||
|
||||
max > 0 && down && start <= index && (n >= limit || n == self.count || start == 0) {
|
||||
if max == 0 && start <= index && (n > 0 || start == 0) || max > 0 && down && start <= index && (n >= limit || n == self.count || start == 0) {
|
||||
break
|
||||
}
|
||||
if down {
|
||||
|
|
@ -415,6 +407,7 @@ func (self *Kademlia) Load(path string, cb func(*NodeRecord, Node) error) (err e
|
|||
}
|
||||
|
||||
// kademlia table + kaddb table displayed with ascii
|
||||
// callerholds the lock
|
||||
func (self *Kademlia) String() string {
|
||||
|
||||
var rows []string
|
||||
|
|
@ -5,10 +5,11 @@ import (
|
|||
"net"
|
||||
"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/storage"
|
||||
"github.com/ethereum/go-ethereum/common/chequebook"
|
||||
"github.com/ethereum/go-ethereum/common/kademlia"
|
||||
)
|
||||
|
||||
/*
|
||||
|
|
@ -108,7 +109,7 @@ type retrieveRequestMsgData struct {
|
|||
MaxSize uint64 // maximum size of delivery accepted
|
||||
MaxPeers uint64 // maximum number of peers returned
|
||||
Timeout uint64 // the longest time we are expecting a response
|
||||
timeout *time.Time // [not serialied]}
|
||||
timeout *time.Time // [not serialied]
|
||||
from *peer //
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +161,9 @@ type peerAddr struct {
|
|||
|
||||
// peerAddr pretty prints as enode
|
||||
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 +234,7 @@ peer/protocol instance when the node is registered by hive as online{
|
|||
*/
|
||||
|
||||
type syncRequestMsgData struct {
|
||||
SyncState *syncState `rlp:"nil"`
|
||||
SyncState *syncState `rlp:"nil" `
|
||||
}
|
||||
|
||||
func (self *syncRequestMsgData) String() string {
|
||||
|
|
|
|||
|
|
@ -1,33 +1,34 @@
|
|||
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
|
||||
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
|
||||
* encoding and decoding requests for storage and retrieval
|
||||
* handling the s§protocol handshake
|
||||
* dispaching to netstore for handling the DHT logic
|
||||
* registering peers in the KΛÐΞMLIΛ table via the hive logistic manager
|
||||
* handling sync protocol messages via the syncer
|
||||
* talks the SWAP payent protocol (swap accounting is done within NetStore)
|
||||
The bzz protocol component speaks the bzz protocol
|
||||
* handle the protocol handshake
|
||||
* register peers in the KΛÐΞMLIΛ table via the hive logistic manager
|
||||
* dispatch to hive for handling the DHT logic
|
||||
* encode and decode requests for storage and retrieval
|
||||
* handle sync protocol messages via the syncer
|
||||
* talks the SWAP payment protocol (swap accounting is done within NetStore)
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
bzzswap "github.com/ethereum/go-ethereum/swarm/services/swap"
|
||||
"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/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"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 (
|
||||
|
|
@ -67,7 +68,7 @@ type bzz struct {
|
|||
selfID discover.NodeID // peer's node id used in peer advertising in handshake
|
||||
key storage.Key // baseaddress as storage.Key
|
||||
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
|
||||
requestDb *storage.LDBDatabase // db to persist backlog of deliveries to aid syncing
|
||||
remoteAddr *peerAddr // remote peers address
|
||||
|
|
@ -77,11 +78,11 @@ type bzz struct {
|
|||
|
||||
swap *swap.Swap // swap instance for the peer connection
|
||||
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
|
||||
syncParams *SyncParams // syncer params
|
||||
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
|
||||
|
|
@ -151,7 +152,7 @@ func run(requestDb *storage.LDBDatabase, depo StorageHandler, hive *Hive, dbacce
|
|||
},
|
||||
swapParams: sp,
|
||||
syncParams: sy,
|
||||
swapEnabled: true,
|
||||
swapEnabled: hive.swapEnabled,
|
||||
syncEnabled: true,
|
||||
}
|
||||
|
||||
|
|
@ -174,6 +175,10 @@ func run(requestDb *storage.LDBDatabase, depo StorageHandler, hive *Hive, dbacce
|
|||
|
||||
// the main forever loop that handles incoming requests
|
||||
for {
|
||||
if self.hive.blockRead {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
err = self.handle()
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -268,8 +273,6 @@ func (self *bzz) handle() error {
|
|||
if err != nil {
|
||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
||||
}
|
||||
// set peers state to persist
|
||||
self.syncState = req.State
|
||||
|
||||
case deliveryRequestMsg:
|
||||
// response to syncKeysMsg hashes filtered not existing in db
|
||||
|
|
@ -286,12 +289,14 @@ func (self *bzz) handle() error {
|
|||
|
||||
case paymentMsg:
|
||||
// swap protocol message for payment, Units paid for, Cheque paid with
|
||||
var req paymentMsgData
|
||||
if err := msg.Decode(&req); err != nil {
|
||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
||||
if self.swapEnabled {
|
||||
var req paymentMsgData
|
||||
if err := msg.Decode(&req); err != nil {
|
||||
return self.protoError(ErrDecode, "->msg %v: %v", msg, err)
|
||||
}
|
||||
glog.V(logger.Debug).Infof("[BZZ] incoming 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:
|
||||
// no other message is allowed
|
||||
|
|
@ -361,10 +366,9 @@ func (self *bzz) handleStatus() (err error) {
|
|||
self.hive.addPeer(&peer{bzz: self})
|
||||
|
||||
// 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)
|
||||
self.syncRequest()
|
||||
}
|
||||
glog.V(logger.Info).Infof("[BZZ] syncronisation request sent with %v", self.syncState)
|
||||
self.syncRequest()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -377,9 +381,12 @@ func (self *bzz) sync(state *syncState) error {
|
|||
cnt := self.dbAccess.counter()
|
||||
remoteaddr := self.remoteAddr.Addr
|
||||
start, stop := self.hive.kad.KeyRange(remoteaddr)
|
||||
|
||||
// an explicitly received nil syncstate disables syncronisation
|
||||
if state == nil {
|
||||
state = newSyncState(start, stop, cnt)
|
||||
glog.V(logger.Warn).Infof("[BZZ] peer %08x provided no sync state, setting up full sync: %v\n", remoteaddr[:4], state)
|
||||
self.syncEnabled = false
|
||||
glog.V(logger.Info).Infof("[BZZ] syncronisation disabled for peer %v", self)
|
||||
state = &syncState{DbSyncState: &storage.DbSyncState{}, Synced: true}
|
||||
} else {
|
||||
state.synced = make(chan bool)
|
||||
state.SessionAt = cnt
|
||||
|
|
@ -387,6 +394,7 @@ func (self *bzz) sync(state *syncState) error {
|
|||
state.Start = storage.Key(start[:])
|
||||
state.Stop = storage.Key(stop[:])
|
||||
}
|
||||
glog.V(logger.Detail).Infof("[BZZ] syncronisation requested by peer %v at state %v", self, state)
|
||||
}
|
||||
var err error
|
||||
self.syncer, err = newSyncer(
|
||||
|
|
@ -394,11 +402,12 @@ func (self *bzz) sync(state *syncState) error {
|
|||
storage.Key(remoteaddr[:]),
|
||||
self.dbAccess,
|
||||
self.unsyncedKeys, self.store,
|
||||
self.syncParams, state,
|
||||
self.syncParams, state, func() bool { return self.syncEnabled },
|
||||
)
|
||||
if err != nil {
|
||||
return self.protoError(ErrSync, "%v", err)
|
||||
}
|
||||
glog.V(logger.Detail).Infof("[BZZ] syncer set for peer %v", self)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -443,8 +452,13 @@ func (self *bzz) store(req *storeRequestMsgData) error {
|
|||
}
|
||||
|
||||
func (self *bzz) syncRequest() error {
|
||||
req := &syncRequestMsgData{
|
||||
SyncState: self.syncState,
|
||||
req := &syncRequestMsgData{}
|
||||
if self.hive.syncEnabled {
|
||||
glog.V(logger.Detail).Infof("[BZZ] syncronisation request to peer %v at state %v", self, self.syncState)
|
||||
req.SyncState = self.syncState
|
||||
}
|
||||
if self.syncState == nil {
|
||||
glog.V(logger.Detail).Infof("[BZZ] syncronisation disabled for peer %v at state %v", self, self.syncState)
|
||||
}
|
||||
return self.send(syncRequestMsg, req)
|
||||
}
|
||||
|
|
@ -496,6 +510,9 @@ func (self *bzz) protoErrorDisconnect(err *errs.Error) {
|
|||
}
|
||||
|
||||
func (self *bzz) send(msg uint64, data interface{}) error {
|
||||
if self.hive.blockWrite {
|
||||
return fmt.Errorf("network write blocked")
|
||||
}
|
||||
glog.V(logger.Debug).Infof("[BZZ] -> %v: %v (%T) to %v", msg, data, data, self)
|
||||
err := p2p.Send(self.rw, msg, data)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/kademlia"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"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
|
||||
type syncer struct {
|
||||
*SyncParams // sync parameters
|
||||
syncF func() bool // if syncing is needed
|
||||
key storage.Key // remote peers address key
|
||||
state *syncState // sync state for our dbStore
|
||||
syncStates chan *syncState // different stages of sync
|
||||
|
|
@ -165,6 +165,7 @@ func newSyncer(
|
|||
store func(*storeRequestMsgData) error,
|
||||
params *SyncParams,
|
||||
state *syncState,
|
||||
syncF func() bool,
|
||||
) (*syncer, error) {
|
||||
|
||||
syncBufferSize := params.SyncBufferSize
|
||||
|
|
@ -172,6 +173,7 @@ func newSyncer(
|
|||
dbBatchSize := params.RequestDbBatchSize
|
||||
|
||||
self := &syncer{
|
||||
syncF: syncF,
|
||||
key: remotekey,
|
||||
dbAccess: dbAccess,
|
||||
syncStates: make(chan *syncState, 20),
|
||||
|
|
@ -191,35 +193,19 @@ func newSyncer(
|
|||
// initialise a syncdb instance for each priority queue
|
||||
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)
|
||||
// launch chunk delivery service
|
||||
go self.syncDeliveries()
|
||||
// launch sync task manager
|
||||
go self.sync()
|
||||
if self.syncF() {
|
||||
go self.sync()
|
||||
}
|
||||
// process unsynced keys to broadcast
|
||||
go self.syncUnsyncedKeys()
|
||||
|
||||
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
|
||||
func encodeSync(state *syncState) (*json.RawMessage, error) {
|
||||
data, err := json.MarshalIndent(state, "", " ")
|
||||
|
|
@ -238,7 +224,7 @@ func decodeSync(meta *json.RawMessage) (*syncState, error) {
|
|||
if len(data) == 0 {
|
||||
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)
|
||||
return state, err
|
||||
}
|
||||
|
|
@ -478,6 +464,7 @@ LOOP:
|
|||
glog.V(logger.Warn).Infof("[BZZ] syncer[%v]: unable to send unsynced keys: %v", err)
|
||||
}
|
||||
unsynced = nil
|
||||
keys = nil
|
||||
}
|
||||
|
||||
// process item and add it to the batch
|
||||
|
|
@ -503,11 +490,11 @@ LOOP:
|
|||
deliveryRequest = nil
|
||||
|
||||
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
|
||||
// signals that data is available to send if peer is ready to receive
|
||||
newUnsyncedKeys = nil
|
||||
// this can only happen if keys is
|
||||
keys = self.keys[High]
|
||||
|
||||
case state, more = <-syncStates:
|
||||
// this resets the state
|
||||
|
|
@ -620,13 +607,16 @@ func (self *syncer) syncDeliveries() {
|
|||
If sync mode is off then, requests are directly sent to deliveries
|
||||
*/
|
||||
func (self *syncer) addRequest(req interface{}, ty int) {
|
||||
// retrieve priority for request type
|
||||
// retrieve priority for request type name int8
|
||||
|
||||
priority := self.SyncPriorities[ty]
|
||||
// sync mode for this type ON
|
||||
if self.SyncModes[ty] {
|
||||
self.addKey(req, priority, self.quit)
|
||||
} else {
|
||||
self.addDelivery(req, priority, self.quit)
|
||||
if self.syncF() || ty == DeliverReq {
|
||||
if self.SyncModes[ty] {
|
||||
self.addKey(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
|
||||
|
||||
import (
|
||||
|
|
@ -11,14 +12,20 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"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/logger"
|
||||
"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
|
||||
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
|
||||
*/
|
||||
|
||||
const (
|
||||
gasToCash = "2000000" // gas cost of a cash transaction using chequebook
|
||||
getSentAbiPre = "d75d691d" // sent amount accessor in the chequebook contract
|
||||
cashAbiPre = "fbf788d6" // abi preamble signature for cash method of the chequebook
|
||||
queryInterval = 15000000000 // 15 seconds
|
||||
deployGas = "3000000"
|
||||
// confirmationInterval = 3 * 10 * *11 // 5 minutes
|
||||
var (
|
||||
gasToCash = big.NewInt(2000000) // gas cost of a cash transaction using chequebook
|
||||
// gasToDeploy = big.NewInt(3000000)
|
||||
)
|
||||
|
||||
// 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
|
||||
type Cheque struct {
|
||||
// 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)
|
||||
}
|
||||
|
||||
type Params struct {
|
||||
ContractCode, ContractAbi, ContractSource string
|
||||
}
|
||||
|
||||
var ContractParams = &Params{ContractCode, ContractAbi, ContractSource}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
type Params struct {
|
||||
ContractCode, ContractAbi string
|
||||
}
|
||||
|
||||
var ContractParams = &Params{contract.ChequebookBin, contract.ChequebookABI}
|
||||
|
||||
// chequebook to create, sign cheques from single contract to multiple beneficiarys
|
||||
// outgoing payment handler for peer to peer micropayments
|
||||
type Chequebook struct {
|
||||
path string // path to chequebook file
|
||||
prvKey *ecdsa.PrivateKey // private key to sign cheque with
|
||||
lock sync.Mutex //
|
||||
backend Backend // blockchain API
|
||||
quit chan bool // when closed causes autodeposit to stop
|
||||
owner common.Address // owner address (derived from pubkey)
|
||||
backend bind.Backend // blockchain API
|
||||
// abigen bind.ContractBackend // blockchain contract backend for abigen
|
||||
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
|
||||
balance *big.Int // not synced with blockchain
|
||||
contract common.Address // contract address
|
||||
sent map[common.Address]*big.Int //tallies for beneficiarys
|
||||
balance *big.Int // not synced with blockchain
|
||||
contractAddr common.Address // contract address
|
||||
sent map[common.Address]*big.Int //tallies for beneficiarys
|
||||
|
||||
txhash string // tx hash of last deposit tx
|
||||
threshold *big.Int // threshold that triggers autodeposit if not nil
|
||||
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) {
|
||||
if (owner == common.Address{}) {
|
||||
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 (self *Chequebook) String() string {
|
||||
return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey)
|
||||
}
|
||||
|
||||
func Validate(contract common.Address, backend Backend) (err error) {
|
||||
if (contract == common.Address{}) {
|
||||
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) {
|
||||
// NewChequebook(path, contract, prvKey, abibbackend, backend) creates a new Chequebook
|
||||
func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend bind.Backend) (self *Chequebook, err error) {
|
||||
balance := new(big.Int)
|
||||
sent := make(map[common.Address]*big.Int)
|
||||
owner := crypto.PubkeyToAddress(prvKey.PublicKey)
|
||||
self = &Chequebook{
|
||||
balance: balance,
|
||||
contract: contract,
|
||||
sent: sent,
|
||||
path: path,
|
||||
prvKey: prvKey,
|
||||
backend: backend,
|
||||
owner: owner,
|
||||
|
||||
chbook, err := contract.NewChequebook(contractAddr, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if (contract != common.Address{}) {
|
||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] new chequebook initialised from %v (owner: %v)", contract.Hex(), owner.Hex())
|
||||
transactOpts := bind.NewKeyedTransactor(prvKey)
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
data, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
|
|
@ -184,7 +149,11 @@ func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend) (sel
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
|
@ -207,7 +176,7 @@ func (self *Chequebook) UnmarshalJSON(data []byte) error {
|
|||
if !ok {
|
||||
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 {
|
||||
self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10)
|
||||
if !ok {
|
||||
|
|
@ -220,7 +189,7 @@ func (self *Chequebook) UnmarshalJSON(data []byte) error {
|
|||
func (self *Chequebook) MarshalJSON() ([]byte, error) {
|
||||
var file = &chequebookFile{
|
||||
Balance: self.balance.String(),
|
||||
Contract: self.contract.Hex(),
|
||||
Contract: self.contractAddr.Hex(),
|
||||
Owner: self.owner.Hex(),
|
||||
Sent: make(map[string]string),
|
||||
}
|
||||
|
|
@ -238,7 +207,7 @@ func (self *Chequebook) Save() (err error) {
|
|||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
|
@ -259,6 +228,7 @@ func (self *Chequebook) Stop() {
|
|||
func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
glog.V(logger.Detail).Infof("[CHEQUEBOOK] prvKey: %v", self.prvKey)
|
||||
if amount.Cmp(common.Big0) <= 0 {
|
||||
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.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 {
|
||||
ch = &Cheque{
|
||||
Contract: self.contract,
|
||||
Contract: self.contractAddr,
|
||||
Beneficiary: beneficiary,
|
||||
Amount: sum,
|
||||
Sig: sig,
|
||||
|
|
@ -301,7 +272,7 @@ func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *
|
|||
|
||||
// convenience method to cash any cheque
|
||||
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
|
||||
|
|
@ -330,13 +301,13 @@ func (self *Chequebook) Owner() common.Address {
|
|||
}
|
||||
|
||||
// Backend() public accessor for backend
|
||||
func (self *Chequebook) Backend() Backend {
|
||||
func (self *Chequebook) Backend() bind.Backend {
|
||||
return self.backend
|
||||
}
|
||||
|
||||
// Address() public accessor for contract
|
||||
func (self *Chequebook) Address() common.Address {
|
||||
return self.contract
|
||||
return self.contractAddr
|
||||
}
|
||||
|
||||
// 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
|
||||
// caller holds the lock
|
||||
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
|
||||
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 {
|
||||
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
|
||||
|
|
@ -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())
|
||||
}
|
||||
|
||||
// type ChequeQueue struct {
|
||||
// beneficiary common.Address
|
||||
// last map[string]*Inbox
|
||||
// }
|
||||
|
||||
// inbox to deposit, verify and cash cheques
|
||||
// from a single contract to single beneficiary
|
||||
// incoming payment handler for peer to peer micropayments
|
||||
type Inbox struct {
|
||||
lock sync.Mutex
|
||||
contract common.Address // peer's chequebook contract
|
||||
beneficiary common.Address // local peer's receiving address
|
||||
sender common.Address // local peer's address to send cashing tx from
|
||||
signer *ecdsa.PublicKey // peer's public key
|
||||
txhash string // tx hash of last cashing tx
|
||||
backend Backend // blockchain API
|
||||
quit chan bool // when closed causes autocash to stop
|
||||
maxUncashed *big.Int // threshold that triggers autocashing
|
||||
cashed *big.Int // cumulative amount cashed
|
||||
cheque *Cheque // last cheque, nil if none yet received
|
||||
contract common.Address // peer's chequebook contract
|
||||
beneficiary common.Address // local peer's receiving address
|
||||
sender common.Address // local peer's address to send cashing tx from
|
||||
signer *ecdsa.PublicKey // peer's public key
|
||||
txhash string // tx hash of last cashing tx
|
||||
abigen bind.ContractBackend // blockchain API
|
||||
session *contract.ChequebookSession // abi contract backend with tx opts
|
||||
quit chan bool // when closed causes autocash to stop
|
||||
maxUncashed *big.Int // threshold that triggers autocashing
|
||||
cashed *big.Int // cumulative amount cashed
|
||||
cheque *Cheque // last cheque, nil if none yet received
|
||||
}
|
||||
|
||||
// NewInbox(contract, beneficiary, signer, backend) constructor for Inbox
|
||||
// not persisted, cumulative sum updated from blockchain when first cheque received
|
||||
// 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 {
|
||||
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{
|
||||
contract: contract,
|
||||
contract: contractAddr,
|
||||
beneficiary: beneficiary,
|
||||
sender: sender,
|
||||
signer: signer,
|
||||
backend: backend,
|
||||
session: session,
|
||||
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))
|
||||
|
|
@ -494,7 +476,7 @@ func (self *Inbox) Stop() {
|
|||
|
||||
func (self *Inbox) Cash() (txhash string, err error) {
|
||||
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())
|
||||
self.cashed = self.cheque.Amount
|
||||
}
|
||||
|
|
@ -548,7 +530,7 @@ func (self *Inbox) autoCash(cashInterval time.Duration) {
|
|||
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) {
|
||||
ch := promise.(*Cheque)
|
||||
|
||||
|
|
@ -557,18 +539,12 @@ func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
|||
var sum *big.Int
|
||||
if self.cheque == nil {
|
||||
// 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 {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
sum = tally
|
||||
} else {
|
||||
sum = self.cheque.Amount
|
||||
}
|
||||
|
|
@ -590,37 +566,6 @@ func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
|||
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
|
||||
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)
|
||||
|
|
@ -653,8 +598,21 @@ func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary com
|
|||
return amount, nil
|
||||
}
|
||||
|
||||
// Cash(backend) will cash the check using xeth backend to send a transaction
|
||||
// Beneficiary address should be unlocked
|
||||
func (self *Cheque) Cash(sender common.Address, backend Backend) (string, error) {
|
||||
return backend.Transact(sender.Hex(), self.Contract.Hex(), "", "", "", gasToCash, self.cashAbiEncode())
|
||||
// v/r/s representation of signature
|
||||
func sig2vrs(sig []byte) (v *big.Int, r, s [32]byte) {
|
||||
v = big.NewInt(int64(sig[64] + 27))
|
||||
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
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"testing"
|
||||
"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/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"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 {
|
||||
calls []string
|
||||
errs []error
|
||||
txs []string
|
||||
*backends.SimulatedBackend
|
||||
}
|
||||
|
||||
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 {
|
||||
return &testBackend{}
|
||||
}
|
||||
|
||||
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
|
||||
accs := accounts()
|
||||
return &testBackend{SimulatedBackend: backends.NewSimulatedBackend(accs...)}
|
||||
}
|
||||
|
||||
func (b *testBackend) GetTxReceipt(txhash common.Hash) *types.Receipt {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *testBackend) CodeAt(address string) string {
|
||||
func (b *testBackend) CodeAt(address common.Address) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func genAddr() common.Address {
|
||||
prvKey, _ := crypto.GenerateKey()
|
||||
return crypto.PubkeyToAddress(prvKey.PublicKey)
|
||||
func (b *testBackend) BalanceAt(address common.Address) *big.Int {
|
||||
return big.NewInt(0)
|
||||
}
|
||||
|
||||
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) {
|
||||
prvKey, _ := crypto.GenerateKey()
|
||||
sender := genAddr()
|
||||
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 {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
t.Fatalf("deploy contract: expected no error, got %v", err)
|
||||
}
|
||||
recipient := genAddr()
|
||||
chbook.sent[recipient] = new(big.Int).SetUint64(42)
|
||||
chbook, err := NewChequebook(path, addr0, key0, backend)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
chbook.sent[addr1] = new(big.Int).SetUint64(42)
|
||||
amount := common.Big1
|
||||
ch, err := chbook.Issue(recipient, amount)
|
||||
ch, err := chbook.Issue(addr1, amount)
|
||||
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)
|
||||
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 {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if chbook.Balance().Cmp(common.Big0) != 0 {
|
||||
t.Errorf("expected: %v, got %v", "0", chbook.Balance())
|
||||
}
|
||||
|
||||
backend := newTestBackend()
|
||||
backend.calls = []string{common.ToHex(big.NewInt(42).Bytes())}
|
||||
backend.errs = []error{nil}
|
||||
chbox, err := NewInbox(sender, recipient, recipient, &prvKey.PublicKey, backend)
|
||||
chbox, err := NewInbox(key1, addr0, addr1, &key0.PublicKey, backend)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
if received.Cmp(common.Big1) != 0 {
|
||||
t.Errorf("expected: %v, got %v", "1", received)
|
||||
if received.Cmp(big.NewInt(43)) != 0 {
|
||||
t.Errorf("expected: %v, got %v", "43", received)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCheckbookFile(t *testing.T) {
|
||||
prvKey, _ := crypto.GenerateKey()
|
||||
sender := genAddr()
|
||||
path := "/tmp/checkbook.json"
|
||||
chbook, err := NewChequebook(path, sender, prvKey, nil)
|
||||
backend := newTestBackend()
|
||||
chbook, err := NewChequebook(path, addr0, key0, backend)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
recipient := genAddr()
|
||||
chbook.sent[recipient] = new(big.Int).SetUint64(42)
|
||||
chbook.sent[addr1] = new(big.Int).SetUint64(42)
|
||||
chbook.balance = new(big.Int).Set(common.Big1)
|
||||
|
||||
chbook.Save()
|
||||
|
||||
chbook, err = LoadChequebook(path, prvKey, nil)
|
||||
chbook, err = LoadChequebook(path, key0, backend, false)
|
||||
if err != nil {
|
||||
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())
|
||||
}
|
||||
|
||||
ch, err := chbook.Issue(recipient, common.Big1)
|
||||
ch, err := chbook.Issue(addr1, common.Big1)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
|
@ -135,31 +148,35 @@ func TestCheckbookFile(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestVerifyErrors(t *testing.T) {
|
||||
prvKey, _ := crypto.GenerateKey()
|
||||
sender0 := genAddr()
|
||||
sender1 := genAddr()
|
||||
path0 := "/tmp/checkbook0.json"
|
||||
chbook0, err := NewChequebook(path0, sender0, prvKey, nil)
|
||||
backend := newTestBackend()
|
||||
contr0, err := deploy(key0, common.Big2, backend.SimulatedBackend)
|
||||
if err != nil {
|
||||
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"
|
||||
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 {
|
||||
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)
|
||||
chbook1.balance = new(big.Int).Set(common.Big1)
|
||||
chbook0.sent[recipient0] = new(big.Int).SetUint64(42)
|
||||
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()
|
||||
backend.calls = []string{common.ToHex(big.NewInt(42).Bytes())}
|
||||
backend.errs = []error{nil}
|
||||
chbox, err := NewInbox(sender0, recipient0, recipient0, &prvKey.PublicKey, backend)
|
||||
time.Sleep(5)
|
||||
chbox, err := NewInbox(key1, contr0, addr1, &key0.PublicKey, backend)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
if received.Cmp(common.Big1) != 0 {
|
||||
t.Errorf("expected: %v, got %v", "1", received)
|
||||
if received.Cmp(big.NewInt(43)) != 0 {
|
||||
t.Errorf("expected: %v, got %v", "43", received)
|
||||
}
|
||||
|
||||
ch1, err := chbook0.Issue(recipient1, amount)
|
||||
ch1, err := chbook0.Issue(addr2, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
received, err = chbox.Receive(ch1)
|
||||
t.Log(err)
|
||||
t.Logf("correct error: %v", err)
|
||||
if err == nil {
|
||||
t.Fatalf("expected receiver error, got none")
|
||||
}
|
||||
|
||||
ch2, err := chbook1.Issue(recipient0, amount)
|
||||
ch2, err := chbook1.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
received, err = chbox.Receive(ch2)
|
||||
t.Log(err)
|
||||
t.Logf("correct error: %v", err)
|
||||
if err == nil {
|
||||
t.Fatalf("expected sender error, got none")
|
||||
}
|
||||
|
||||
_, err = chbook1.Issue(recipient0, new(big.Int).SetInt64(-1))
|
||||
t.Log(err)
|
||||
_, err = chbook1.Issue(addr1, new(big.Int).SetInt64(-1))
|
||||
t.Logf("correct error: %v", err)
|
||||
if err == nil {
|
||||
t.Fatalf("expected incorrect amount error, got none")
|
||||
}
|
||||
|
||||
received, err = chbox.Receive(ch0)
|
||||
t.Log(err)
|
||||
t.Logf("correct error: %v", err)
|
||||
if err == nil {
|
||||
t.Fatalf("expected incorrect amount error, got none")
|
||||
}
|
||||
|
|
@ -209,31 +226,27 @@ func TestVerifyErrors(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDeposit(t *testing.T) {
|
||||
prvKey, _ := crypto.GenerateKey()
|
||||
sender := genAddr()
|
||||
path := "/tmp/checkbook.json"
|
||||
|
||||
path0 := "/tmp/checkbook0.json"
|
||||
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 {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
balance := new(big.Int).SetUint64(42)
|
||||
chbook.Deposit(balance)
|
||||
if len(backend.txs) != 1 {
|
||||
t.Fatalf("expected 1 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
backend.Commit()
|
||||
if chbook.balance.Cmp(balance) != 0 {
|
||||
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
||||
}
|
||||
|
||||
recipient := genAddr()
|
||||
amount := common.Big1
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
exp := new(big.Int).SetUint64(41)
|
||||
if chbook.balance.Cmp(exp) != 0 {
|
||||
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
||||
|
|
@ -241,36 +254,33 @@ func TestDeposit(t *testing.T) {
|
|||
|
||||
// autodeposit on each issue
|
||||
chbook.AutoDeposit(0, balance, balance)
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
backend.Commit()
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if len(backend.txs) != 3 {
|
||||
t.Fatalf("expected 3 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
backend.Commit()
|
||||
if chbook.balance.Cmp(balance) != 0 {
|
||||
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
||||
}
|
||||
|
||||
// autodeposit off
|
||||
chbook.AutoDeposit(0, common.Big0, balance)
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
backend.Commit()
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
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)
|
||||
if chbook.balance.Cmp(exp) != 0 {
|
||||
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
|
||||
interval := 30 * time.Millisecond
|
||||
chbook.AutoDeposit(interval, common.Big1, balance)
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
backend.Commit()
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
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)
|
||||
if chbook.balance.Cmp(exp) != 0 {
|
||||
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
||||
}
|
||||
|
||||
time.Sleep(3 * interval)
|
||||
if len(backend.txs) != 4 {
|
||||
t.Fatalf("expected 4 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
backend.Commit()
|
||||
if chbook.balance.Cmp(balance) != 0 {
|
||||
t.Fatalf("expected balance %v, got %v", balance, chbook.balance)
|
||||
}
|
||||
|
||||
exp = new(big.Int).SetUint64(40)
|
||||
chbook.AutoDeposit(4*interval, exp, balance)
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
backend.Commit()
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(3 * interval)
|
||||
if len(backend.txs) != 4 {
|
||||
t.Fatalf("expected 4 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
backend.Commit()
|
||||
if chbook.balance.Cmp(exp) != 0 {
|
||||
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
||||
}
|
||||
|
||||
_, err = chbook.Issue(recipient, amount)
|
||||
_, err = chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
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 {
|
||||
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.Stop()
|
||||
|
||||
_, err = chbook.Issue(recipient, common.Big1)
|
||||
_, err = chbook.Issue(addr1, common.Big1)
|
||||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
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)
|
||||
if chbook.balance.Cmp(exp) != 0 {
|
||||
t.Fatalf("expected balance %v, got %v", exp, chbook.balance)
|
||||
|
|
@ -362,26 +364,22 @@ func TestDeposit(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCash(t *testing.T) {
|
||||
prvKey, _ := crypto.GenerateKey()
|
||||
sender := genAddr()
|
||||
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 {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
recipient := genAddr()
|
||||
chbook.sent[recipient] = new(big.Int).SetUint64(42)
|
||||
chbook.sent[addr1] = new(big.Int).SetUint64(42)
|
||||
amount := 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 {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
backend := newTestBackend()
|
||||
backend.calls = []string{common.ToHex(big.NewInt(42).Bytes())}
|
||||
backend.errs = []error{nil}
|
||||
chbox, err := NewInbox(sender, recipient, recipient, &prvKey.PublicKey, backend)
|
||||
backend.Commit()
|
||||
chbox, err := NewInbox(key1, contr0, addr1, &key0.PublicKey, backend)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
|
@ -391,20 +389,20 @@ func TestCash(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
_, err = ch.Cash(recipient, backend)
|
||||
if len(backend.txs) != 1 {
|
||||
t.Fatalf("expected 1 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
_, err = ch.Cash(chbook.session)
|
||||
backend.Commit()
|
||||
|
||||
chbook.balance = new(big.Int).Set(common.Big3)
|
||||
ch0, err := chbook.Issue(recipient, amount)
|
||||
ch0, err := chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
interval := 10 * time.Millisecond
|
||||
// setting autocash with interval of 10ms
|
||||
|
|
@ -417,82 +415,119 @@ func TestCash(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
// after < interval time and 2 cheques received, no new cashing tx is sent
|
||||
if len(backend.txs) != 1 {
|
||||
t.Fatalf("expected 1 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
backend.Commit()
|
||||
// expBalance := big.NewInt(2)
|
||||
// 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
|
||||
time.Sleep(4 * interval)
|
||||
if len(backend.txs) != 2 {
|
||||
t.Fatalf("expected 2 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
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)
|
||||
// }
|
||||
|
||||
// after stopping autocash no more tx are sent
|
||||
ch2, err := chbook.Issue(recipient, amount)
|
||||
ch2, err := chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
chbox.Stop()
|
||||
time.Sleep(interval) // make sure loop stops
|
||||
_, err = chbox.Receive(ch2)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
time.Sleep(2 * interval)
|
||||
if len(backend.txs) != 2 {
|
||||
t.Fatalf("expected 2 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
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)
|
||||
// }
|
||||
|
||||
// autocash below 1
|
||||
chbook.balance = new(big.Int).Set(common.Big2)
|
||||
chbook.balance = big.NewInt(2)
|
||||
chbox.AutoCash(0, common.Big1)
|
||||
|
||||
ch3, err := chbook.Issue(recipient, amount)
|
||||
ch3, err := chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
_, err = chbox.Receive(ch3)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
_, err = chbox.Receive(ch4)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// 2 checks of amount 1 received, exactly 1 tx is sent
|
||||
if len(backend.txs) != 3 {
|
||||
t.Fatalf("expected 3 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
// expBalance = big.NewInt(6)
|
||||
// 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
|
||||
chbook.balance = new(big.Int).Set(common.Big2)
|
||||
chbox.AutoCash(0, common.Big0)
|
||||
|
||||
ch5, err := chbook.Issue(recipient, amount)
|
||||
ch5, err := chbook.Issue(addr1, amount)
|
||||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
_, err = chbox.Receive(ch5)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if len(backend.txs) != 5 {
|
||||
t.Fatalf("expected 5 txs to send, got %v", len(backend.txs))
|
||||
}
|
||||
backend.Commit()
|
||||
// expBalance = big.NewInt(6)
|
||||
// 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>
|
||||
contract chequebook is mortal {
|
||||
// Cumulative paid amount in wei to each beneficiary
|
||||
mapping (address => uint256) sent;
|
||||
mapping (address => uint256) public 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) constant returns (uint256) {
|
||||
return sent[beneficiary];
|
||||
}
|
||||
|
||||
/// @notice Cash cheque
|
||||
///
|
||||
/// @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"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/chequebook"
|
||||
"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/logger"
|
||||
"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
|
||||
|
|
@ -31,14 +34,9 @@ var (
|
|||
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)
|
||||
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)
|
||||
|
||||
maxRetries = 5
|
||||
)
|
||||
|
||||
var (
|
||||
retryInterval = 10 * time.Second
|
||||
)
|
||||
|
||||
type SwapParams struct {
|
||||
|
|
@ -52,14 +50,14 @@ type SwapProfile struct {
|
|||
}
|
||||
|
||||
type PayProfile struct {
|
||||
PublicKey string // check againsst signature of promise
|
||||
Contract common.Address // address of chequebook contract
|
||||
Beneficiary common.Address // recipient address for swarm sales revenue
|
||||
privateKey *ecdsa.PrivateKey `json:"-"`
|
||||
publicKey *ecdsa.PublicKey `json:"-"`
|
||||
PublicKey string // check against signature of promise
|
||||
Contract common.Address // address of chequebook contract
|
||||
Beneficiary common.Address // recipient address for swarm sales revenue
|
||||
privateKey *ecdsa.PrivateKey
|
||||
publicKey *ecdsa.PublicKey
|
||||
owner common.Address
|
||||
chbook *chequebook.Chequebook `json:"-"`
|
||||
backend chequebook.Backend
|
||||
chbook *chequebook.Chequebook
|
||||
backend bind.Backend
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
|
|
@ -107,12 +105,12 @@ func NewSwap(local *SwapParams, remote *SwapProfile, proto swap.Protocol) (self
|
|||
// insolvent chequebooks suicide so will signal as invalid
|
||||
// TODO: monitoring a chequebooks events
|
||||
var in *chequebook.Inbox
|
||||
err = chequebook.Validate(remote.Contract, local.backend)
|
||||
err = bind.Validate(remote.Contract, contract.ContractDeployedCode, local.backend)
|
||||
if err != nil {
|
||||
glog.V(logger.Info).Infof("[BZZ] SWAP invalid contract %v for peer %v: %v)", remote.Contract.Hex()[:8], proto, err)
|
||||
} else {
|
||||
// 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)), local.backend)
|
||||
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)
|
||||
}
|
||||
|
|
@ -120,7 +118,7 @@ func NewSwap(local *SwapParams, remote *SwapProfile, proto swap.Protocol) (self
|
|||
|
||||
// cheque if local chequebook contract is valid
|
||||
var out *chequebook.Outbox
|
||||
err = chequebook.Validate(local.Contract, local.backend)
|
||||
err = bind.Validate(local.Contract, contract.ContractDeployedCode, local.backend)
|
||||
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)
|
||||
} else {
|
||||
|
|
@ -161,6 +159,10 @@ func (self *SwapParams) Chequebook() *chequebook.Chequebook {
|
|||
return self.chbook
|
||||
}
|
||||
|
||||
func (self *SwapParams) Backend() bind.Backend {
|
||||
return self.backend
|
||||
}
|
||||
|
||||
func (self *SwapParams) PrivateKey() *ecdsa.PrivateKey {
|
||||
return self.privateKey
|
||||
}
|
||||
|
|
@ -173,23 +175,17 @@ func (self *SwapParams) SetKey(prvkey *ecdsa.PrivateKey) {
|
|||
self.publicKey = &prvkey.PublicKey
|
||||
}
|
||||
|
||||
const (
|
||||
confirmationInterval = 60000000000
|
||||
timeout = 30000000000 // 30 sec
|
||||
)
|
||||
|
||||
// setChequebook(path, backend) wraps the
|
||||
// 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()
|
||||
self.lock.Lock()
|
||||
var valid bool
|
||||
done = make(chan bool)
|
||||
self.backend = backend
|
||||
err = chequebook.Validate(self.Contract, backend)
|
||||
err = bind.Validate(self.Contract, contract.ContractDeployedCode, backend)
|
||||
if err != nil {
|
||||
owner := crypto.PubkeyToAddress(*(self.publicKey))
|
||||
go self.deployChequebook(owner, path, done)
|
||||
go self.deployChequebook(path, done)
|
||||
} else {
|
||||
valid = true
|
||||
go func() {
|
||||
|
|
@ -204,40 +200,39 @@ func (self *SwapParams) SetChequebook(path string, backend chequebook.Backend) (
|
|||
return done, nil
|
||||
}
|
||||
|
||||
func (self *SwapParams) deployChequebook(owner common.Address, path string, done chan bool) {
|
||||
var timer = time.NewTimer(0).C
|
||||
retries := 0
|
||||
var err error
|
||||
func deploy(deployTransactor *bind.TransactOpts, backend bind.ContractBackend) (*types.Transaction, error) {
|
||||
_, tx, _, err := contract.DeployChequebook(deployTransactor, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// prvKey *ecdsa.PrivateKey, amount *big.Int,
|
||||
|
||||
func (self *SwapParams) deployChequebook(path string, 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
|
||||
OUT:
|
||||
for {
|
||||
select {
|
||||
case <-timer:
|
||||
// this is blocking
|
||||
glog.V(logger.Info).Infof("[BZZ] SWAP Deploying new chequebook (owner: %v)", owner.Hex())
|
||||
var contract common.Address
|
||||
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)
|
||||
if retries >= maxRetries {
|
||||
glog.V(logger.Info).Infof("[BZZ] SWAP unable to deploy new chequebook: giving up after %v retries", retries)
|
||||
break OUT
|
||||
}
|
||||
retries++
|
||||
timer = time.NewTicker(retryInterval).C
|
||||
} else {
|
||||
// need to save config at this point
|
||||
self.lock.Lock()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
transactOpts := bind.NewKeyedTransactor(self.privateKey)
|
||||
transactOpts.Value = self.AutoDepositBuffer
|
||||
contract, err := bind.Deploy(deploy, contract.ContractDeployedCode, transactOpts, bind.DefaultDeployOptions(), self.backend)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("[BZZ] SWAP unable to deploy new chequebook: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// need to save config at this point
|
||||
self.lock.Lock()
|
||||
self.Contract = contract
|
||||
err = self.newChequebookFromContract(path, self.backend)
|
||||
self.lock.Unlock()
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infof("[BZZ] SWAP error initialising cheque book (owner: %v): %v", owner.Hex(), err)
|
||||
} else {
|
||||
valid = true
|
||||
glog.V(logger.Info).Infof("[BZZ] SWAP new chequebook deployed at %v (owner: %v)", contract.Hex(), owner.Hex())
|
||||
}
|
||||
done <- valid
|
||||
close(done)
|
||||
|
|
@ -245,8 +240,7 @@ OUT:
|
|||
|
||||
// initialise the chequebook from a persisted json file or create a new one
|
||||
// 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())
|
||||
err := os.MkdirAll(filepath.Join(path, "chequebooks"), os.ModePerm)
|
||||
if err != nil {
|
||||
|
|
@ -254,7 +248,7 @@ func (self *SwapParams) newChequebookFromContract(path string, backend chequeboo
|
|||
}
|
||||
|
||||
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 {
|
||||
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.balance += n
|
||||
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()
|
||||
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 {
|
||||
glog.V(logger.Detail).Infof("[SWAP] <%v> we cannot have debt (unable to buy)", self.proto, self.balance)
|
||||
return fmt.Errorf("[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 (balance: %v)", self.proto, self.balance)
|
||||
}
|
||||
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)
|
||||
|
|
@ -17,7 +17,7 @@ contract Swarm
|
|||
}
|
||||
|
||||
mapping (address => Bee) swarm;
|
||||
|
||||
|
||||
// block number of transactions presenting chunks
|
||||
mapping (bytes32 => uint) presentedChunks;
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ contract Swarm
|
|||
b.reporter = msg.sender;
|
||||
Report(signer);
|
||||
}
|
||||
|
||||
|
||||
/// @notice Present a chunk in order to avoid losing deposit.
|
||||
///
|
||||
/// @param chunk chunk data
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ func (self *TreeChunker) KeySize() int64 {
|
|||
|
||||
// String() for pretty printing
|
||||
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
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package storage
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/compression/rle"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||
|
|
@ -82,15 +81,3 @@ func (self *LDBDatabase) Close() {
|
|||
// Close the leveldb database
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
156
swarm/swarm.go
156
swarm/swarm.go
|
|
@ -2,12 +2,14 @@ package swarm
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"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/registrar/ethreg"
|
||||
"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"
|
||||
|
|
@ -15,27 +17,62 @@ import (
|
|||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"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/services/chequebook"
|
||||
"github.com/ethereum/go-ethereum/swarm/services/ens"
|
||||
"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
|
||||
type Swarm struct {
|
||||
config *api.Config // swarm configuration
|
||||
api *api.Api // high level api layer (fs/manifest)
|
||||
dbAccess *network.DbAccess // access to local chunk db iterator and storage counter
|
||||
storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends
|
||||
dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support
|
||||
depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage
|
||||
cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud)
|
||||
hive *network.Hive // the logistic manager
|
||||
client *httpclient.HTTPClient // bzz capable light http client
|
||||
ethereum *eth.Ethereum
|
||||
config *api.Config // swarm configuration
|
||||
api *api.Api // high level api layer (fs/manifest)
|
||||
dns api.Resolver // DNS registrar
|
||||
dbAccess *network.DbAccess // access to local chunk db iterator and storage counter
|
||||
storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends
|
||||
dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support
|
||||
depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage
|
||||
cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud)
|
||||
hive *network.Hive // the logistic manager
|
||||
client *httpclient.HTTPClient // bzz capable light http client
|
||||
contractBackend bind.ContractBackend // abigen contract Backend
|
||||
backend bind.Backend // simple blockchain Backend
|
||||
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.config.Swap.Backend(),
|
||||
PrvKey: self.config.Swap.PrivateKey(),
|
||||
}
|
||||
}
|
||||
|
||||
type Backend interface {
|
||||
GetTxReceipt(txhash common.Hash) *types.Receipt
|
||||
BalanceAt(address common.Address) *big.Int
|
||||
}
|
||||
|
||||
// creates a new swarm service instance
|
||||
// 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) {
|
||||
return nil, fmt.Errorf("empty public key")
|
||||
|
|
@ -45,21 +82,24 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool)
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
self = &Swarm{
|
||||
config: config,
|
||||
client: ethereum.HTTPClient(),
|
||||
config: config,
|
||||
ethereum: ethereum,
|
||||
swapEnabled: swapEnabled,
|
||||
client: ethereum.HTTPClient(),
|
||||
}
|
||||
glog.V(logger.Debug).Infof("[BZZ] Setting up Swarm service components")
|
||||
|
||||
// setup local store
|
||||
hash := storage.MakeHashFunc(config.ChunkerParams.Hash)
|
||||
lstore, err := storage.NewLocalStore(hash, config.StoreParams)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// setup local store
|
||||
glog.V(logger.Debug).Infof("[BZZ] Set up local storage")
|
||||
|
||||
self.dbAccess = network.NewDbAccess(lstore)
|
||||
|
|
@ -69,6 +109,8 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool)
|
|||
self.hive = network.NewHive(
|
||||
common.HexToHash(self.config.BzzKey), // key to hive (kademlia base address)
|
||||
config.HiveParams, // configuration parameters
|
||||
swapEnabled, // SWAP enabled
|
||||
syncEnabled, // syncronisation enabled
|
||||
)
|
||||
glog.V(logger.Debug).Infof("[BZZ] Set up swarm network with Kademlia hive")
|
||||
|
||||
|
|
@ -78,9 +120,9 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool)
|
|||
// setup cloud storage internal access layer
|
||||
|
||||
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)
|
||||
glog.V(logger.Debug).Infof("[BZZ] -> REmote Access to CHunks")
|
||||
|
||||
|
|
@ -89,23 +131,22 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool)
|
|||
glog.V(logger.Debug).Infof("[BZZ] -> Local Access to Swarm")
|
||||
// Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage
|
||||
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
|
||||
backend := api.NewEthApi(ethereum)
|
||||
backend.UpdateState()
|
||||
self.api = api.NewApi(self.dpa, ethreg.New(backend), self.config)
|
||||
// Manifests for Smart Hosting
|
||||
glog.V(logger.Debug).Infof("[BZZ] -> Level 2: Collection/Directory API")
|
||||
transactOpts := bind.NewKeyedTransactor(self.config.Swap.PrivateKey())
|
||||
// backend := ethereum.ContractBackend()
|
||||
self.dns = ens.NewENS(transactOpts, ENSContractAddr, self.backend)
|
||||
glog.V(logger.Debug).Infof("[BZZ] -> Swarm Domain Name Registrar")
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +170,16 @@ func (self *Swarm) Start(net *p2p.Server) error {
|
|||
net.AddPeer(node)
|
||||
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")
|
||||
self.hive.Start(
|
||||
|
|
@ -143,7 +194,7 @@ func (self *Swarm) Start(net *p2p.Server) error {
|
|||
|
||||
// start swarm http proxy server
|
||||
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)
|
||||
|
||||
|
|
@ -154,7 +205,7 @@ func (self *Swarm) Start(net *p2p.Server) error {
|
|||
"bzz": self.config.Port,
|
||||
}
|
||||
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)
|
||||
|
||||
|
|
@ -182,20 +233,37 @@ func (self *Swarm) Protocols() []p2p.Protocol {
|
|||
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 {
|
||||
return self.api
|
||||
}
|
||||
|
||||
// Backend interface implemented by eth or JSON-IPC client
|
||||
func (self *Swarm) SetChequebook(backend chequebook.Backend) (err error) {
|
||||
done, err := self.config.Swap.SetChequebook(self.config.Path, backend)
|
||||
//
|
||||
func (self *Swarm) SetChequebook() (err error) {
|
||||
done, err := self.config.Swap.SetChequebook(self.config.Path, self.backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
ok := <-done
|
||||
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.hive.DropAll()
|
||||
}
|
||||
|
|
@ -223,9 +291,19 @@ func NewLocalSwarm(datadir, port string) (self *Swarm, err error) {
|
|||
}
|
||||
|
||||
self = &Swarm{
|
||||
api: api.NewApi(dpa, nil, config),
|
||||
api: api.NewApi(dpa, nil),
|
||||
config: config,
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
echo "Upload file '$1' to node $2 on port 85$2" 1>&2
|
||||
key=`bash swarm/cmd/bzzup.sh $1 85$2`
|
||||
echo -n $key
|
||||
}
|
||||
dir=`dirname $0`
|
||||
source $dir/../../cmd/swarm/tesst.sh
|
||||
|
||||
function down { #key port
|
||||
echo "Download hash '$1' from node $2 on port 85$2"
|
||||
wget -O- http://localhost:85$2/$1 > /dev/null && echo "got it" || echo "not found"
|
||||
}
|
||||
mkdir -p /tmp/swarm-test-files
|
||||
FILE_00=/tmp/swarm-test-files/00
|
||||
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
|
||||
echo "Clean up for $1"
|
||||
rm -rf ~/tmp/sync/$1/{bzz/*/chunks,bzz/*/requests/,bzz/*/bzz-peers.json,chaindata,nodes}
|
||||
}
|
||||
for f in $FILE_00 $FILE_01 $FILE_02 $FILE_03 $FILE_04; do
|
||||
randomfile 20 > $f
|
||||
done
|
||||
# options="--verbosity=0 --vmodule=swarm/network/*=6,common/chequebook/*=6,common/swap/*=6,common/kademlia/*=5"
|
||||
|
||||
function gethup { #index account
|
||||
cp geth geth$1
|
||||
echo "start node $1"
|
||||
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 &"
|
||||
./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
|
||||
}
|
||||
key=/tmp/key
|
||||
swarm init 2 $options
|
||||
swarm info 00
|
||||
swarm info 01
|
||||
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
|
||||
killall -INT geth$1
|
||||
}
|
||||
# exit 1;
|
||||
|
||||
wait=5
|
||||
gethdown 00
|
||||
gethdown 01
|
||||
swarm up 00 $FILE_01|tail -n1 > $key
|
||||
swarm needs 00 $key $FILE_01
|
||||
swarm start 01 $options
|
||||
swarm needs 01 $key $FILE_01
|
||||
|
||||
# ./geth --datadir ~/tmp/sync/00 --password <(echo bzz) account new
|
||||
BZZKEY00=5c6332e46a095feb9da1ed9803af2fa425f96aa6
|
||||
BZZKEY01=7dafa7436cba4b5b94a0452557b20b01d23bdc05
|
||||
echo "Fresh start, wipe datadir"
|
||||
clean 00
|
||||
clean 01
|
||||
swarm up 00 $FILE_02|tail -n1 > $key
|
||||
swarm needs 00 $key $FILE_02
|
||||
swarm needs 01 $key $FILE_02
|
||||
|
||||
gethup 00 $BZZKEY00 &
|
||||
sleep 2
|
||||
echo "beyond\n"
|
||||
key=$(up COPYING.LESSER 00)
|
||||
echo "beyond\n"
|
||||
down $key 00
|
||||
swarm up 01 $FILE_03|tail -n1 > $key
|
||||
swarm needs 01 $key $FILE_03
|
||||
swarm needs 00 $key $FILE_03
|
||||
|
||||
echo "beyond\n"
|
||||
gethup 01 $BZZKEY01 &
|
||||
sleep $wait
|
||||
down $key 01
|
||||
gethdown 01
|
||||
swarm stop 00
|
||||
swarm up 01 $FILE_04|tail -n1 > $key
|
||||
swarm needs 01 $key $FILE_04
|
||||
swarm start 00 #--bzznoswap
|
||||
swarm needs 00 $key $FILE_04
|
||||
|
||||
|
||||
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
|
||||
swarm stop all
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
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