Skip to content

Commit

Permalink
10 create get fct to retrieve website on the blockchain (#45)
Browse files Browse the repository at this point in the history
* Add Fetch and GetOwner functions to website pkg

* Add zipper pkg with GetFileFromZip function

* Update CLI main to show owner and index.html from downloaded website

* Update .gitignore to exclude website archives

* Improve Chunk tests and add some test cases

* Remove useless error of prepareUploadParams function

* Update website read.go to use FinalValue instead of CandidateValue
  • Loading branch information
thomas-senechal authored Jul 25, 2024
1 parent 9e773c9 commit 93a4f7a
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 8 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ build

*.log

# Websites archives
*.zip

# MacOS
.DS_Store
35 changes: 35 additions & 0 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/massalabs/DeWeb/int/config"
"github.com/massalabs/DeWeb/int/utils"
"github.com/massalabs/DeWeb/int/zipper"
pkgConfig "github.com/massalabs/DeWeb/pkg/config"
"github.com/massalabs/DeWeb/pkg/website"
"github.com/massalabs/station/pkg/logger"
Expand All @@ -32,6 +33,40 @@ func main() {
}

logger.Infof("Website uploaded successfully to address: %s", address)

owner, err := website.GetOwner(&config.NetworkInfos, address)
if err != nil {
logger.Fatalf("failed to get website owner: %v", err)
}

logger.Infof("Website owner: %s", owner)

websiteBytes, err := website.Fetch(&config.NetworkInfos, address)
if err != nil {
logger.Fatalf("failed to fetch website: %v", err)
}

logger.Infof("Website fetched successfully with size: %d", len(websiteBytes))

outputZipPath := fmt.Sprintf("website_%s.zip", address)

err = os.WriteFile(outputZipPath, websiteBytes, 0o644)
if err != nil {
logger.Error("Failed to write website zip file", err)
return
}

logger.Info("Website successfully written to file: %s", outputZipPath)

fileName := "index.html"

content, err := zipper.GetFileFromZip(outputZipPath, fileName)
if err != nil {
logger.Error(err)
return
}

logger.Infof("%s content:\n %s", fileName, content)
}

func deployWebsite(config *pkgConfig.Config) (string, error) {
Expand Down
36 changes: 36 additions & 0 deletions int/zipper/zipper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package zipper

import (
"archive/zip"
"fmt"
"io"
)

// GetFileFromZip returns the content of the given file from the zip file.
// It returns an error if the file is not found in the zip.
func GetFileFromZip(zipFilePath, fileName string) ([]byte, error) {
zipReader, err := zip.OpenReader(zipFilePath)
if err != nil {
return []byte{}, fmt.Errorf("failed to open zip file: %w", err)
}
defer zipReader.Close()

for _, file := range zipReader.File {
if file.Name == fileName {
rc, err := file.Open()
if err != nil {
return []byte{}, fmt.Errorf("failed to open file in zip: %w", err)
}
defer rc.Close()

buf, err := io.ReadAll(rc)
if err != nil {
return []byte{}, fmt.Errorf("failed to read file in zip: %w", err)
}

return buf, nil
}
}

return []byte{}, fmt.Errorf("%s not found in zip", fileName)
}
23 changes: 21 additions & 2 deletions pkg/website/chunk_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package website

import (
_ "embed"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -18,6 +19,10 @@ func TestChunk(t *testing.T) {
{"Median", []byte("Hello, World!"), 4},
{"Big byte array", []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit."), 16},
{"Small byte array", []byte("hello"), 32},
{"Single character", []byte("a"), 2},
{"Exact divisible length", []byte("123456"), 2},
{"Long sentence", []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."), 3},
{"Website deployer wasm", sc, ChunkSize},
}

for _, test := range tests {
Expand All @@ -26,13 +31,27 @@ func TestChunk(t *testing.T) {

if test.data == nil || test.chunkSize <= 0 {
assert.Nil(t, chunks)

return
}

expectedLen := (len(test.data) + test.chunkSize - 1) / test.chunkSize

assert.Equal(t, len(chunks), expectedLen)

expectedLastChunkLen := len(test.data) % test.chunkSize
if expectedLastChunkLen == 0 && len(test.data) > 0 {
expectedLastChunkLen = test.chunkSize
}

// Assert the length of each chunk except the last one
for i := 0; i < len(chunks)-1; i++ {
assert.Equal(t, test.chunkSize, len(chunks[i]), "Chunk size does not match the expected value.")
}

// Assert the length of the last chunk
if len(chunks) > 0 {
lastChunk := chunks[len(chunks)-1]
assert.Equal(t, expectedLastChunkLen, len(lastChunk), "The length of the last chunk does not match the expected value.")
}
})
}
}
77 changes: 77 additions & 0 deletions pkg/website/read.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package website

import (
"fmt"

"github.com/massalabs/station/int/config"
"github.com/massalabs/station/pkg/convert"
"github.com/massalabs/station/pkg/node"
)

// Fetch retrieves the complete data of a website as bytes.
func Fetch(network *config.NetworkInfos, websiteAddress string) ([]byte, error) {
client := node.NewClient(network.NodeURL)

chunkNumber, err := getNumberOfChunks(client, websiteAddress)
if err != nil {
return nil, fmt.Errorf("fetching number of chunks: %w", err)
}

dataStore, err := fetchAllChunks(client, websiteAddress, chunkNumber)
if err != nil {
return nil, fmt.Errorf("fetching all chunks: %w", err)
}

return dataStore, nil
}

// getNumberOfChunks fetches and returns the number of chunks for the website.
func getNumberOfChunks(client *node.Client, websiteAddress string) (int32, error) {
nbChunkResponse, err := node.FetchDatastoreEntry(client, websiteAddress, convert.ToBytes(nbChunkKey))
if err != nil {
return 0, fmt.Errorf("fetching website number of chunks: %w", err)
}

chunkNumber, err := convert.BytesToI32(nbChunkResponse.FinalValue)
if err != nil {
return 0, fmt.Errorf("converting fetched data for key '%s': %w", nbChunkKey, err)
}

return chunkNumber, nil
}

// fetchAllChunks retrieves all chunks of data for the website.
func fetchAllChunks(client *node.Client, websiteAddress string, chunkNumber int32) ([]byte, error) {
keys := make([][]byte, chunkNumber)
for i := 0; i < int(chunkNumber); i++ {
keys[i] = convert.I32ToBytes(i)
}

response, err := node.ContractDatastoreEntries(client, websiteAddress, keys)
if err != nil {
return nil, fmt.Errorf("calling get_datastore_entries '%+v': %w", keys, err)
}

if len(response) != int(chunkNumber) {
return nil, fmt.Errorf("expected %d entries, got %d", chunkNumber, len(response))
}

var dataStore []byte
for _, entry := range response {
dataStore = append(dataStore, entry.FinalValue...)
}

return dataStore, nil
}

// GetOwner retrieves the owner of the website.
func GetOwner(network *config.NetworkInfos, websiteAddress string) (string, error) {
client := node.NewClient(network.NodeURL)

ownerResponse, err := node.FetchDatastoreEntry(client, websiteAddress, convert.ToBytes(ownerKey))
if err != nil {
return "", fmt.Errorf("fetching website owner: %w", err)
}

return string(ownerResponse.FinalValue), nil
}
9 changes: 3 additions & 6 deletions pkg/website/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ func UploadChunk(
return "", fmt.Errorf("chunk is empty, no data to upload")
}

params, err := prepareUploadParams(chunk, chunkIndex)
if err != nil {
return "", fmt.Errorf("preparing upload params: %w", err)
}
params := prepareUploadParams(chunk, chunkIndex)

uploadCost, err := ComputeChunkCost(chunkIndex, len(chunk))
if err != nil {
Expand All @@ -45,12 +42,12 @@ func UploadChunk(
return performUpload(config, websiteAddress, params, uploadCost, chunkIndex)
}

func prepareUploadParams(chunk []byte, chunkIndex int) ([]byte, error) {
func prepareUploadParams(chunk []byte, chunkIndex int) []byte {
params := convert.I32ToBytes(chunkIndex)
params = append(params, convert.U32ToBytes(len(chunk))...)
params = append(params, chunk...)

return params, nil
return params
}

func performUpload(
Expand Down

0 comments on commit 93a4f7a

Please sign in to comment.