Skip to content
This repository was archived by the owner on Apr 4, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 113 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ KVVM is served over RPC with [go-plugin](https://github.com/hashicorp/go-plugin)

At its core, the Avalanche protocol still maintains the immutable ordered sequence of states in a fully permissionless settings. And KVVM defines the rules and data structures to store key-value pairs.

To interact with Avalanche network RPC chain APIs, download and run a [AvalancheGo](https://github.com/ava-labs/avalanchego#installation) node locally, as follows:
Build quarkvm:

```bash
cd ${HOME}/go/src/github.com/ava-labs/quarkvm
./scripts/build.sh
```

*Step 1.* To interact with Avalanche network RPC chain APIs, download and run a [AvalancheGo](https://github.com/ava-labs/avalanchego#installation) node locally, as follows:

```bash
# run 1 avalanchego node in local network
Expand All @@ -36,6 +43,110 @@ curl -X POST --data '{
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/health
```

TODO: example commands
*Step 2.* Create a user:

```bash
curl --location --request POST '127.0.0.1:9650/ext/keystore' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"id" :1,
"method" :"keystore.createUser",
"params" :{
"username":"testusername123",
"password":"insecurestring789"
}
}'
```

*Step 3.* Import the pre-funded key for the P-chain:

```bash
curl --location --request POST '127.0.0.1:9650/ext/P' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"id" :1,
"method" :"platform.importKey",
"params" :{
"username":"testusername123",
"password":"insecurestring789",
"privateKey":"PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN"
}
}'
# {"jsonrpc":"2.0","result":{"address":"P-local18jma8ppw3nhx5r4ap8clazz0dps7rv5u00z96u"},"id":1}
```

*Step 4.* Get the list of P-chain addresses:

```bash
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "platform.listAddresses",
"params": {
"username":"testusername123",
"password":"insecurestring789"
},
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/P
# {"jsonrpc":"2.0","result":{"addresses":["P-local18jma8ppw3nhx5r4ap8clazz0dps7rv5u00z96u"]},"id":1}
```

*Step 5.* Create a subnet:

```bash
curl --location --request POST '127.0.0.1:9650/ext/P' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"id" :1,
"method" :"platform.createSubnet",
"params" :{
"username":"testusername123",
"password":"insecurestring789",
"threshold":1,
"controlKeys":["P-local18jma8ppw3nhx5r4ap8clazz0dps7rv5u00z96u"]
}
}'
# {"jsonrpc":"2.0","result":{"txID":"29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL","changeAddr":"P-local18jma8ppw3nhx5r4ap8clazz0dps7rv5u00z96u"},"id":1}
# 29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL is the subnet blockchain ID
```

*Step 6.* Create a blockchain:

```bash
# TODO: where to get vmID
curl --location --request POST '127.0.0.1:9650/ext/P' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"id" :1,
"method" :"platform.createBlockchain",
"params" :{
"username":"testusername123",
"password":"insecurestring789",
"vmID":"tGas3T58KzdjLHhBDMnH2TvrddhqTji5iZAMZ3RXs2NLpSnhH",
"subnetID":"29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL",
"name":"quarkvm",
"genesisData":"",
"controlKeys":["P-local18jma8ppw3nhx5r4ap8clazz0dps7rv5u00z96u"]
}
}'
#
```

Connect to quarkvm:

```bash
curl --location --request POST '127.0.0.1:9650/ext/vm/tGas3T58KzdjLHhBDMnH2TvrddhqTji5iZAMZ3RXs2NLpSnhH' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"id" :1,
"method" :"quarkvm.put",
"params" :{
"key":"foo",
"value":"bar"
}
}'
```
137 changes: 137 additions & 0 deletions agent/agent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// Package agent implements KVVM agent.
package agent

import (
"context"
"fmt"
"math/big"
"math/rand"
"time"

"ekyu.moe/cryptonight"
"github.com/ava-labs/quarkvm/chain"
"github.com/ava-labs/quarkvm/crypto/ed25519"
"github.com/ava-labs/quarkvm/transaction"
)

type Agent interface {
Run()
}

type agent struct {
ctx context.Context
chain chain.Chain

privateKey ed25519.PrivateKey
publicKey ed25519.PublicKey
}

func New(ctx context.Context, chain chain.Chain) Agent {
prv, err := ed25519.NewPrivateKey()
if err != nil {
panic(err)
}
pub := prv.PublicKey()

fmt.Println("new agent:", pub.Address())
return &agent{
ctx: ctx,
chain: chain,

privateKey: prv,
publicKey: pub,
}
}

func (a *agent) Run() {
for a.ctx.Err() == nil {
prefix := randString(16)
if rand.Intn(100) < 20 {
// claim own address key
prefix = a.publicKey.Address()
fmt.Println("attempting to claim address prefix", prefix)
}
utx := a.claim(prefix)
a.mine(utx)
stx := a.sign(utx)
a.chain.Submit(stx)

// wait for claim to be set or abandon
confirmed := a.confirm(stx)
if !confirmed {
// TODO: try again with same prefix
continue
}
owner, _, err := a.chain.GetPrefixInfo([]byte(prefix))
if err != nil {
panic(err)
}
fmt.Println("prefix claimed:", prefix, "expires:", owner.Expiry, "keys:", owner.Keys)
// TODO: print out "rate of decay"
// TODO: set 2 keys
// TODO: delete 1 key
// TODO: wait for key expiry
// TODO: attempt to set key
// TODO: add lifeline
}
}

func (a *agent) claim(prefix string) transaction.Unsigned {
return transaction.NewClaim(a.publicKey, []byte(prefix))
}

func (a *agent) mine(utx transaction.Unsigned) {
for {
cbID := a.chain.CurrentBlock().ID()
utx.SetBlockID(cbID)
graffiti := big.NewInt(0)
for a.chain.ValidBlockID(cbID) {
utx.SetGraffiti(graffiti.Bytes())
h := cryptonight.Sum(transaction.UnsignedBytes(utx), 2)
if cryptonight.CheckHash(h, a.chain.DifficultyEstimate()) {
return
}
graffiti.Add(graffiti, big.NewInt(1))
}
// Get new block hash if no longer valid
}
}

func (a *agent) sign(utx transaction.Unsigned) *transaction.Transaction {
sig, err := a.privateKey.Sign(transaction.UnsignedBytes(utx))
if err != nil {
panic(err)
}
return transaction.New(utx, sig)
}

func (a *agent) confirm(stx *transaction.Transaction) bool {
loops := 0
for a.ctx.Err() == nil && a.chain.ValidBlockID(stx.Unsigned.GetBlockID()) {
if a.chain.TxConfirmed(stx.ID()) {
return true
}
time.Sleep(1 * time.Second)
loops++

// Resubmit if pending for a while but still valid
if loops%5 == 0 && !a.chain.MempoolContains(stx.ID()) {
a.chain.Submit(stx)
}
}
return false
}

func randString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
l := rand.Intn(n) + 1 // ensure never 0

s := make([]rune, l)
for i := range s {
s[i] = letters[rand.Intn(len(letters))]
}
return string(s)
}
Loading