mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
synced up to go-ethereum 1.5.0-unstable develop branch major features: * api overhaul due to rpc v2 and node service stack interface changes * blockchain/ethereum contract interaction rewritten using abi/abigen (chequebook, ens) * swarm - cluster control CLI - migration and revamp of prehistoric eth-utils repo * poor man's end to end testing: scripted scenarios in swarm/test using swarm CLI * http proxy now handles 3 url schemes for 1) ens-enabled [bzz], 2) immutable [bzzi] and 3) raw manifest [bzzr] resolution * fixes issues with remote address setting, forwarding and syncing * new control flags to switch swap and sync on and off * placeholder basic implementation Ethereum Name Service * improved logging - now debug level is coherent regression: * uri based versioning support is dropped temporarily since state tree pruning does not guarantee historical record * registrar related functionality temporarily restricted - current ENS provides basic free and unrestricted Register/Resolve accounts/abi: * bind: repeated attempt deployment of contracts, validation against known code, transactor creation from private keys * accountmanager: getUnlocked snatch private key when unlocked cmd: * unlockAccount moved to utils/cmd and exported * getPassPhrase moved to utils/input and exported * accountcmds: reflect the change * js: GlobalRegistrar is dropped (ens) flags: * chequebook, bzzaccount, bzzport, bzzconfig, bzznoswap, bzznosync chequebook: * move from common/ to swarm/services * abigen-ised * specifies its own API (removed chequebook api from swarm/api) kademlia: * move from common to swarm/network/kademlia * address abstracted out to separate file + tests dns/ens/registrar: * moved from swarm/api to swarm/services/ens * implementation is basic placeholder before ENS is implemented * temporary rpc api via ens namespace * the old common/registrar is removed (also from eth/backend apis) swap: * the abstract swap module moved from common to swarm/services * now embedded in the swarm and chequebook specific setup (this will change) * safer chequebook deployment using abigen helpers * backend not field of swap params eth: * public accessor for GPO, needed to construct a PublicBlockChainAPI * extends ContractBackend in eth/bind.go with BalanceAt, GetTxReceipt and CodeAt API calls internal/web3ext * add js bindings for bzz, chequebook rpc apis swarm/api: * refactored api into smaller modules filesystem/storage/testapi * ethereum backend (needed for dns, swap, etc) moved to abi/bind * TODO: further refactor due to #2040 * swarm/api/http: now supports the 3 uri schemes * examples/album updated swarm/cmd: * migrate old eth-utils and modify into a cluster control CLI * bzzup now allows non-local gateway, endpoint specified as second argument swarm/network: * forwarder improved log messages, fixup condition on whether syncer is nil * hive extended with controls for testing support block read/write, swap/sync enabled/disabled * hive keepAlive launches with alarm in case no discover and no kaddb * fix IP address formatting issue [::1] -> became ::1 which refused to dial, now use discover.NewNode#String * integrate functionality for enabling/disabling sync and swap * allow nil sync state - improve syncer interface in protocol * fix SData slice out of bounds bug 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
257 lines
5.4 KiB
Go
257 lines
5.4 KiB
Go
package api
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/logger"
|
|
"github.com/ethereum/go-ethereum/logger/glog"
|
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
|
)
|
|
|
|
const maxParallelFiles = 5
|
|
|
|
type FileSystem struct {
|
|
api *Api
|
|
}
|
|
|
|
func NewFileSystem(api *Api) *FileSystem {
|
|
return &FileSystem{api}
|
|
}
|
|
|
|
// Upload replicates a local directory as a manifest file and uploads it
|
|
// using dpa store
|
|
// TODO: localpath should point to a manifest
|
|
func (self *FileSystem) Upload(lpath, index string) (string, error) {
|
|
var list []*manifestTrieEntry
|
|
localpath, err := filepath.Abs(filepath.Clean(lpath))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
f, err := os.Open(localpath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
stat, err := f.Stat()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var start int
|
|
if stat.IsDir() {
|
|
start = len(localpath)
|
|
glog.V(logger.Debug).Infof("[BZZ] uploading '%s'", localpath)
|
|
err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error {
|
|
if (err == nil) && !info.IsDir() {
|
|
//fmt.Printf("lp %s path %s\n", localpath, path)
|
|
if len(path) <= start {
|
|
return fmt.Errorf("Path is too short")
|
|
}
|
|
if path[:start] != localpath {
|
|
return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath)
|
|
}
|
|
entry := &manifestTrieEntry{
|
|
Path: path,
|
|
}
|
|
list = append(list, entry)
|
|
}
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
} else {
|
|
dir := filepath.Dir(localpath)
|
|
start = len(dir)
|
|
if len(localpath) <= start {
|
|
return "", fmt.Errorf("Path is too short")
|
|
}
|
|
if localpath[:start] != dir {
|
|
return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir)
|
|
}
|
|
entry := &manifestTrieEntry{
|
|
Path: localpath,
|
|
}
|
|
list = append(list, entry)
|
|
}
|
|
|
|
cnt := len(list)
|
|
errors := make([]error, cnt)
|
|
done := make(chan bool, maxParallelFiles)
|
|
dcnt := 0
|
|
|
|
for i, entry := range list {
|
|
if i >= dcnt+maxParallelFiles {
|
|
<-done
|
|
dcnt++
|
|
}
|
|
go func(i int, entry *manifestTrieEntry, done chan bool) {
|
|
f, err := os.Open(entry.Path)
|
|
if err == nil {
|
|
stat, _ := f.Stat()
|
|
sr := io.NewSectionReader(f, 0, stat.Size())
|
|
wg := &sync.WaitGroup{}
|
|
var hash storage.Key
|
|
hash, err = self.api.dpa.Store(sr, wg)
|
|
if hash != nil {
|
|
list[i].Hash = hash.String()
|
|
}
|
|
wg.Wait()
|
|
if err == nil {
|
|
first512 := make([]byte, 512)
|
|
fread, _ := sr.ReadAt(first512, 0)
|
|
if fread > 0 {
|
|
mimeType := http.DetectContentType(first512[:fread])
|
|
if filepath.Ext(entry.Path) == ".css" {
|
|
mimeType = "text/css"
|
|
}
|
|
list[i].ContentType = mimeType
|
|
}
|
|
}
|
|
f.Close()
|
|
}
|
|
errors[i] = err
|
|
done <- true
|
|
}(i, entry, done)
|
|
}
|
|
for dcnt < cnt {
|
|
<-done
|
|
dcnt++
|
|
}
|
|
|
|
trie := &manifestTrie{
|
|
dpa: self.api.dpa,
|
|
}
|
|
for i, entry := range list {
|
|
if errors[i] != nil {
|
|
return "", errors[i]
|
|
}
|
|
entry.Path = RegularSlashes(entry.Path[start:])
|
|
if entry.Path == index {
|
|
ientry := &manifestTrieEntry{
|
|
Path: "",
|
|
Hash: entry.Hash,
|
|
ContentType: entry.ContentType,
|
|
}
|
|
trie.addEntry(ientry)
|
|
}
|
|
trie.addEntry(entry)
|
|
}
|
|
|
|
err2 := trie.recalcAndStore()
|
|
var hs string
|
|
if err2 == nil {
|
|
hs = trie.hash.String()
|
|
}
|
|
return hs, err2
|
|
}
|
|
|
|
// Download replicates the manifest path structure on the local filesystem
|
|
// under localpath
|
|
func (self *FileSystem) Download(bzzpath, localpath string) error {
|
|
lpath, err := filepath.Abs(filepath.Clean(localpath))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = os.MkdirAll(lpath, os.ModePerm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//resolving host and port
|
|
key, _, path, err := self.api.parseAndResolve(bzzpath, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// if len(path) > 0 {
|
|
// path += "/"
|
|
// }
|
|
|
|
trie, err := loadManifest(self.api.dpa, key)
|
|
if err != nil {
|
|
glog.V(logger.Warn).Infof("[BZZ] fs.Download: loadManifestTrie error: %v", err)
|
|
return err
|
|
}
|
|
|
|
type downloadListEntry struct {
|
|
key storage.Key
|
|
path string
|
|
}
|
|
|
|
var list []*downloadListEntry
|
|
var mde, mderr error
|
|
|
|
prevPath := lpath
|
|
err = trie.listWithPrefix(path, func(entry *manifestTrieEntry, suffix string) { // TODO: paralellize
|
|
glog.V(logger.Detail).Infof("[BZZ] fs.Download: %#v", entry)
|
|
|
|
key := common.Hex2Bytes(entry.Hash)
|
|
path := lpath + "/" + suffix
|
|
dir := filepath.Dir(path)
|
|
if dir != prevPath {
|
|
mde = os.MkdirAll(dir, os.ModePerm)
|
|
if mde != nil {
|
|
mderr = mde
|
|
}
|
|
prevPath = dir
|
|
}
|
|
if (mde == nil) && (path != dir+"/") {
|
|
list = append(list, &downloadListEntry{key: key, path: path})
|
|
}
|
|
})
|
|
if err == nil {
|
|
err = mderr
|
|
}
|
|
|
|
cnt := len(list)
|
|
errors := make([]error, cnt)
|
|
done := make(chan bool, maxParallelFiles)
|
|
dcnt := 0
|
|
|
|
for i, entry := range list {
|
|
if i >= dcnt+maxParallelFiles {
|
|
<-done
|
|
dcnt++
|
|
}
|
|
go func(i int, entry *downloadListEntry, done chan bool) {
|
|
f, err := os.Create(entry.path) // TODO: path separators
|
|
if err == nil {
|
|
reader := self.api.dpa.Retrieve(entry.key)
|
|
writer := bufio.NewWriter(f)
|
|
_, err = io.CopyN(writer, reader, reader.Size()) // TODO: handle errors
|
|
err2 := writer.Flush()
|
|
if err == nil {
|
|
err = err2
|
|
}
|
|
err2 = f.Close()
|
|
if err == nil {
|
|
err = err2
|
|
}
|
|
}
|
|
|
|
errors[i] = err
|
|
done <- true
|
|
}(i, entry, done)
|
|
}
|
|
for dcnt < cnt {
|
|
<-done
|
|
dcnt++
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i, _ := range list {
|
|
if errors[i] != nil {
|
|
return errors[i]
|
|
}
|
|
}
|
|
return err
|
|
}
|