Skip to content

Commit

Permalink
feature: CreateOracle, ListenOracleQueries higher level convenience f…
Browse files Browse the repository at this point in the history
…unctions. New naet.Node capability required: GetOracleQueriesByPubkey
  • Loading branch information
randomshinichi committed Jan 21, 2020
1 parent 9217c01 commit 8b3d66d
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 4 deletions.
3 changes: 1 addition & 2 deletions aeternity/aens.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ import (
// RegisterName allows one to easily register a name on AENS. It does the
// preclaim, transaction sending, confirmation and claim for you.
func RegisterName(n naet.NodeInterface, acc *account.Account, name string, nameFee *big.Int) (signedTxStr, hash, signature string, blockHeight uint64, blockHash string, err error) {
status, err := n.GetStatus()
networkID, err := getNetworkID(n)
if err != nil {
return
}
networkID := *status.NetworkID
_, _, ttlnoncer := transactions.GenerateTTLNoncer(n)

preclaimTx, nameSalt, err := transactions.NewNamePreclaimTx(acc.Address, name, ttlnoncer)
Expand Down
7 changes: 5 additions & 2 deletions aeternity/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@ type compileencoder interface {

// CreateContract lets one deploy a contract with minimum fuss.
func CreateContract(n naet.NodeInterface, c compileencoder, acc *account.Account, source, function string, args []string, backend string) (signedTxStr, hash, signature string, blockHeight uint64, blockHash string, err error) {
status, err := n.GetStatus()
networkID, err := getNetworkID(n)
if err != nil {
return
}
networkID := *status.NetworkID

bytecode, err := c.CompileContract(source, backend)
if err != nil {
Expand All @@ -44,6 +43,10 @@ func CreateContract(n naet.NodeInterface, c compileencoder, acc *account.Account
return
}

status, err := n.GetStatus()
if err != nil {
return
}
VMVersion, ABIVersion, err := findVMABIVersion(*status.NodeVersion, backend)
if err != nil {
return
Expand Down
9 changes: 9 additions & 0 deletions aeternity/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,12 @@ func SignBroadcastWaitTransaction(tx transactions.Transaction, signingAccount *a
blockHeight, blockHash, err = WaitForTransactionForXBlocks(n, hash, x)
return
}

func getNetworkID(n naet.NodeInterface) (networkID string, err error) {
status, err := n.GetStatus()
if err != nil {
return
}
networkID = *status.NetworkID
return
}
57 changes: 57 additions & 0 deletions aeternity/oracles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package aeternity

import (
"math/big"
"strings"
"time"

"github.com/aeternity/aepp-sdk-go/v7/account"
"github.com/aeternity/aepp-sdk-go/v7/config"
"github.com/aeternity/aepp-sdk-go/v7/naet"
"github.com/aeternity/aepp-sdk-go/v7/swagguard/node/models"
"github.com/aeternity/aepp-sdk-go/v7/transactions"
)

// CreateOracle registers a new oracle with the given queryspec and responsespec
func CreateOracle(n naet.NodeInterface, oracleAccount *account.Account, queryspec, responsespec string, queryFee *big.Int, queryTTLType uint64, oracleTTL uint64) (oracleID string, err error) {
var oraclizer = strings.NewReplacer("ak_", "ok_")
networkID, err := getNetworkID(n)
if err != nil {
return
}
_, _, ttlnoncer := transactions.GenerateTTLNoncer(n)
registerTx, err := transactions.NewOracleRegisterTx(oracleAccount.Address, queryspec, responsespec, queryFee, queryTTLType, oracleTTL, config.Client.Oracles.ABIVersion, ttlnoncer)
if err != nil {
return
}

_, _, _, _, _, err = SignBroadcastWaitTransaction(registerTx, oracleAccount, n, networkID, config.Client.WaitBlocks)
if err != nil {
return
}
return oraclizer.Replace(oracleAccount.Address), nil
}

// ListenOracleQueries polls the node at a custom interval and returns queries
// and errors in their respective channels. listenInterval should be specified
// in milliseconds.
func ListenOracleQueries(n naet.GetOracleQueriesByPubkeyer, oracleID string, queryChan chan *models.OracleQuery, errChan chan error, listenInterval time.Duration) (err error) {
// Node always returns all queries, but keeping track of until where we read
// last iteration ensures we only report newly arriving queries. This means
// the first time this loop runs, it will always return all the queries to
// an oracle.
var readUntilPosition int
for {
oQueries, err := n.GetOracleQueriesByPubkey(oracleID)
if err != nil {
errChan <- err
} else {
for _, q := range oQueries.OracleQueries[readUntilPosition:] {
queryChan <- q
readUntilPosition++
}
}

time.Sleep(listenInterval * time.Millisecond)
}
}
87 changes: 87 additions & 0 deletions aeternity/oracles_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package aeternity

import (
"fmt"
"testing"
"time"

"github.com/aeternity/aepp-sdk-go/v7/swagguard/node/models"
)

type mockOracleQueryNode struct {
oracleQueries *models.OracleQueries
Count int
}

func newMockOracleQueryNode() *mockOracleQueryNode {
return &mockOracleQueryNode{
oracleQueries: &models.OracleQueries{
OracleQueries: []*models.OracleQuery{},
},
}
}
func (m *mockOracleQueryNode) GetOracleQueriesByPubkey(pubkey string) (oracleQueries *models.OracleQueries, err error) {
return m.oracleQueries, err
}
func newOracleQuery(i int) (q *models.OracleQuery, err error) {
q = new(models.OracleQuery)
qJSON := fmt.Sprintf(`{"fee":%d,"id":"oq_FAKEQUERY","oracle_id":"ok_FAKEID","query":"ov_FAKEQUERY=","response":"or_FAKERESPONSE","response_ttl":{"type":"delta","value":100},"sender_id":"ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi","sender_nonce":%d,"ttl":137}`, i, i)
err = q.UnmarshalBinary([]byte(qJSON))
return
}

func (m *mockOracleQueryNode) AddOracleQuery(n int) (err error) {
// Create some fake OracleQuerys to add them to
// mockOracleQueryNode.OracleQueries. Keep track of how many we have
// created.
for index := 0; index < n; index++ {
q, err := newOracleQuery(m.Count)
if err != nil {
break
}
m.Count++
m.oracleQueries.OracleQueries = append(m.oracleQueries.OracleQueries, q)
}
return
}

func TestListenOracleQueries(t *testing.T) {
n := newMockOracleQueryNode()

oQueries := make(chan *models.OracleQuery, 30)
errChan := make(chan error)
go ListenOracleQueries(n, "ok_FAKEID", oQueries, errChan, 20)
n.AddOracleQuery(3)
n.AddOracleQuery(5)
time.Sleep(20 * time.Millisecond)
n.AddOracleQuery(15)
time.Sleep(20 * time.Millisecond)
t.Logf("oQueries channel contains %d queries; we generated %d\n", len(oQueries), n.Count)
if n.Count != len(oQueries) {
t.Fatalf("Oracle Queries channel should contain %d queries but actually had %d", n.Count, len(oQueries))
}
}

func TestManyPendingOracleQueries(t *testing.T) {
n := newMockOracleQueryNode()
readQueries := []*models.OracleQuery{}
oQueries := make(chan *models.OracleQuery, 5)
errChan := make(chan error)

n.AddOracleQuery(100)

go ListenOracleQueries(n, "ok_FAKEID", oQueries, errChan, 20)

var q *models.OracleQuery
for i := 0; i < 100; i++ {
q = <-oQueries
readQueries = append(readQueries, q)
}

if len(oQueries) != 0 {
t.Fatalf("oQueries channel should be empty but still contains %d queries", len(oQueries))
}
if len(readQueries) != 100 {
t.Fatalf("We should have 100 queries in readQueries but instead we have %d queries", len(readQueries))
}
}
7 changes: 7 additions & 0 deletions naet/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type NodeInterface interface {
GetHeighter
GetKeyBlockByHasher
GetOracleByPubkeyer
GetOracleQueriesByPubkeyer
GetGenerationByHeighter
GetTransactionByHasher
GetNameEntryByNamer
Expand Down Expand Up @@ -288,6 +289,12 @@ func (c *Node) GetOracleByPubkey(pubkey string) (oracle *models.RegisteredOracle
return
}

// GetOracleQueriesByPubkeyer guarantees that one can run a GetOracleByPubkey() method
// on the mocked/real network connection
type GetOracleQueriesByPubkeyer interface {
GetOracleQueriesByPubkey(pubkey string) (oracleQueries *models.OracleQueries, err error)
}

// GetOracleQueriesByPubkey get a list of queries made to a particular oracle
func (c *Node) GetOracleQueriesByPubkey(pubkey string) (oracleQueries *models.OracleQueries, err error) {
p := external.NewGetOracleQueriesByPubkeyParams().WithPubkey(pubkey)
Expand Down

0 comments on commit 8b3d66d

Please sign in to comment.