Skip to content
This repository was archived by the owner on May 30, 2022. It is now read-only.

Commit c818611

Browse files
authored
Merge pull request #1 from Dev43/feat/batcher
Feat/batcher
2 parents 860b3a3 + e76d541 commit c818611

File tree

12 files changed

+724
-136
lines changed

12 files changed

+724
-136
lines changed

README.md

Lines changed: 156 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,187 @@
55
[![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](https://godoc.org/github.com/Dev43/arweave-go)
66
[![Go Report Card](https://goreportcard.com/badge/github.com/Dev43/arweave-go)](https://goreportcard.com/report/github.com/Dev43/arweave-go)
77

8-
Golang SDK for the Arweave client.
8+
Golang Client to interact with the Arweave Blockchain.
99

10-
Example of use:
10+
## Usage
11+
12+
### Wallet
13+
14+
In the current version, you can load the Arweave wallet file created from the Arweave server or the plugin.
1115

1216
```golang
17+
// create a new wallet instance
18+
w := wallet.NewWallet()
19+
// extract the key from the wallet instance
20+
err = w.LoadKeyFromFile("./arweave.json")
21+
if err != nil {
22+
//...
23+
}
24+
```
25+
26+
You can directly load the key by using it's filepath or pass it as an array of bytes using `LoadKey([]byte)`.
27+
28+
With the wallet struct, you can sign and verify a message:
29+
30+
```golang
31+
// sign the message "example"
32+
msg := []byte("example")
33+
sig, err := w.Sign(msg))
34+
if err != nil {
35+
//...
36+
}
37+
err = w.Verify(msg, sig)
38+
if err != nil {
39+
// message signature is not valid...
40+
}
41+
// message signature is valid
42+
```
43+
44+
### API
45+
46+
You can call all of the Arweave HTTP api endpoints using the api package. First you must give it the node url or IP address.
47+
48+
```golang
49+
ipAddress := "127.0.0.1"
50+
c, err := api.Dial(ipAddress)
51+
if err != nil {
52+
// problem connecting
53+
}
54+
```
55+
56+
To call the endpoints, you will need to pass in a context.
57+
58+
```golang
59+
c.GetBalance(context.TODO(), "1seRanklLU_1VTGkEk7P0xAwMJfA7owA1JHW5KyZKlY")
60+
```
1361

14-
// create a new arweave client
15-
ar, err := transactor.NewTransactor("209.97.142.169")
16-
if err != nil {
17-
log.Fatal(err)
18-
}
62+
### Transactions
63+
64+
To create a new transaction, you will need to interact with the `transactor` package. The transactor package has 3 main functions, creating, sending and waiting for a transaction.
65+
66+
```golang
67+
// create a new transactor client
68+
ar, err := transactor.NewTransactor("127.0.0.1")
69+
if err != nil {
70+
//...
71+
}
1972

2073
// create a new wallet instance
2174
w := wallet.NewWallet()
2275
// extract the key from the wallet instance
23-
err = w.ExtractKey("./arweave.json")
76+
err = w.LoadKeyFromFile("./arweave.json")
2477
if err != nil {
25-
log.Fatal(err)
78+
//...
2679
}
2780
// create a transaction
28-
txBuilder, err := ar.CreateTransaction(context.TODO(), w, "0", []byte(""), "xblmNxr6cqDT0z7QIWBCo8V0UfJLd3CRDffDhF5Uh9g")
81+
txBuilder, err := ar.CreateTransaction(context.TODO(), w, "0", []byte(""), "1seRanklLU_1VTGkEk7P0xAwMJfA7owA1JHW5KyZKlY")
2982
if err != nil {
30-
log.Fatal(err)
83+
//...
3184
}
32-
85+
3386
// sign the transaction
3487
txn, err := txBuilder.Sign(w)
3588
if err != nil {
36-
log.Fatal(err)
89+
//...
3790
}
3891

3992
// send the transaction
4093
resp, err := ar.SendTransaction(context.TODO(), txn)
4194
if err != nil {
42-
log.Fatal(err)
95+
//...
4396
}
4497

45-
fmt.Println(resp)
46-
4798
// wait for the transaction to get mined
48-
pendingTx, err := ar.WaitMined(context.TODO(), txn)
99+
finalTx, err := ar.WaitMined(context.TODO(), txn)
100+
if err != nil {
101+
//...
102+
}
103+
// get the hash of the transaction
104+
fmt.Println(finalTx.Hash())
105+
```
106+
107+
### Batching, chunking and recombining
108+
109+
Arweave currently has a 3MB transaction limit. To let you be able to upload more than 3MB of data, this library lets you chunk your data into multiples chunks, and upload these chunks to the weave. These chunks are all backlinked to their previous chunk (except the first one) so all you need to use to retrieve all the chunks is the last chunk's address (the "tip" of this linked list).
110+
111+
So far the library has been tested with Tarred and Gzipped files (it's a good idea to compress your files, you'll be paying less fees to the network!)
112+
113+
#### Creating chunks
114+
115+
To create chunks, use the `chunker` package. Currently the chunker takes 500KB chunks, encodes it to the same encoding as the Arweave (Raw Base64 URL encoding) and adds it to a transaction.
116+
117+
```golang
118+
f, err := os.Open("example.tar.gz")
49119
if err != nil {
50-
log.Fatal(err)
120+
//...
51121
}
52-
fmt.Println(pendingTx)
122+
info, err := f.Stat()
123+
if err != nil {
124+
//...
125+
}
126+
chunker, err := chunker.NewChunker(f, info.Size())
127+
if err != nil {
128+
//...
129+
}
130+
chunks, err := chunker.ChunkAll()
131+
if err != nil {
132+
//...
133+
}
134+
```
135+
136+
#### Sending a batch of transactions
137+
138+
To directly do both the chunking and the batch sending of transaction, you can use the `batchchunker` package. The package needs to wait for a single transaction to be mined before sending a new one. This will take a significant amount of time as the Arweave has a ~2 minute block time.
139+
140+
```golang
141+
f, err := os.Open("example.tar.gz")
142+
if err != nil {
143+
//...
144+
}
145+
info, err := f.Stat()
146+
if err != nil {
147+
//...
148+
}
149+
150+
// creates a new batch
151+
newB := batchchunker.NewBatch(ar, w, f, info.Size())
152+
153+
// sends all the transactions
154+
list, err := newB.SendBatchTransaction()
155+
if err != nil {
156+
//...
157+
}
158+
```
159+
160+
#### Recombining
161+
162+
Recombining all the chunks needs to be done from the tip of the batch chain (the last transaction sent). The function will grab all the chunks and recombine it into an io.Writer.
163+
164+
```golang
165+
// in this example, we will save the data as a file
166+
f, err := os.Create("example.tar.gz")
167+
if err != nil {
168+
//...
169+
}
170+
// create a new combiner
171+
newBCombiner := combiner.NewBatchCombiner(ar.Client)
53172

173+
// grab all the chunks starting from chunk at this address
174+
liveChunks, err := newBCombiner.GetAllChunks("fBwTnPGNSAtHTNr6pGvqiYpLmBvTjjJOu3kBxuuit1c")
175+
if err != nil {
176+
//...
177+
}
178+
// recombine all the chunks into our file
179+
err = combiner.Recombine(liveChunks, f)
180+
if err != nil {
181+
//...
182+
}
54183
```
184+
185+
If you actually run the last example, it will retrieve a folder with the Bitcoin, Ethereum and Arweave whitepaper
186+
187+
188+
If you enjoy the library, please consider donating:
189+
190+
- **Arweave Address**: `pfJXiTwwjQwSJF9VT1ZK6kauvobWuKKLUzjz29R1gbQ`
191+
- **Ethereum Address**: `0x3E42b8b399dca71b5c004921Fc6eFfa8dDc9409d`

batchchunker/batchchunker.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package batchchunker
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"strings"
9+
10+
"github.com/Dev43/arweave-go"
11+
"github.com/Dev43/arweave-go/chunker"
12+
"github.com/Dev43/arweave-go/transactor"
13+
"github.com/Dev43/arweave-go/wallet"
14+
)
15+
16+
const chunkerVersion = "0.0.1"
17+
18+
// BatchMaker struct
19+
type BatchMaker struct {
20+
ar *transactor.Transactor
21+
wallet *wallet.Wallet
22+
reader io.Reader
23+
totalSize int64
24+
}
25+
26+
// ChunkInformation is the extra data we add to the tags to inform us about the last chunk, it's position, whether it's the
27+
// head of the chunk and it's data version
28+
type ChunkInformation struct {
29+
PreviousChunk string `json:"previous_chunk"`
30+
IsHead bool `json:"is_head"`
31+
Version string `json:"version"`
32+
Position int64 `json:"position"`
33+
}
34+
35+
// NewBatch creates a NewBatch struct
36+
func NewBatch(ar *transactor.Transactor, w *wallet.Wallet, reader io.Reader, totalSize int64) *BatchMaker {
37+
return &BatchMaker{
38+
ar: ar,
39+
wallet: w,
40+
reader: reader,
41+
totalSize: totalSize,
42+
}
43+
}
44+
45+
// SendBatchTransaction chunks, sends and waits for all the transactions to get mined
46+
func (b *BatchMaker) SendBatchTransaction() ([]string, error) {
47+
txList := []string{}
48+
ch, err := chunker.NewChunker(b.reader, b.totalSize)
49+
if err != nil {
50+
return nil, err
51+
}
52+
for i := int64(0); i < ch.TotalChunks(); i++ {
53+
chunk, err := ch.Next()
54+
if err != nil {
55+
return nil, err
56+
}
57+
data, err := json.Marshal(chunk)
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
txBuilder, err := b.ar.CreateTransaction(context.TODO(), b.wallet, "0", data, "")
63+
if err != nil {
64+
return nil, err
65+
}
66+
previousChunk := ""
67+
if len(txList) > 0 {
68+
previousChunk = txList[len(txList)-1]
69+
}
70+
isHead := false
71+
if i+1 == ch.TotalChunks() {
72+
isHead = true
73+
}
74+
chunkerInfo := ChunkInformation{
75+
PreviousChunk: previousChunk,
76+
IsHead: isHead,
77+
Version: chunkerVersion,
78+
Position: chunk.Position,
79+
}
80+
tagValue, err := json.Marshal(chunkerInfo)
81+
if err != nil {
82+
return nil, err
83+
}
84+
txBuilder.AddTag(arweave.BatchChunkerAppName, string(tagValue))
85+
tx, err := txBuilder.Sign(b.wallet)
86+
if err != nil {
87+
return nil, err
88+
}
89+
resp, err := b.ar.SendTransaction(context.TODO(), tx)
90+
if err != nil {
91+
return nil, err
92+
}
93+
fmt.Println(resp)
94+
minedTx, err := b.ar.WaitMined(context.TODO(), tx)
95+
txList = append(txList, minedTx.Hash())
96+
fmt.Printf("Successfully sent transaction %d/%d with hash %s \n", chunk.Position+1, ch.TotalChunks(), minedTx.Hash())
97+
98+
}
99+
fmt.Printf("Successfully sent batch transactions with head transaction %s and list of transactions: \n - %s \n", txList[len(txList)-1], strings.Join(txList, "\n - "))
100+
101+
return txList, nil
102+
}

0 commit comments

Comments
 (0)