Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Oracles work, AENS mostly works #57

Merged
merged 26 commits into from
Apr 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a2d6743
docs: add minor comments on exported functions
randomshinichi Apr 10, 2019
bce9a4a
chore: test genesis account is now richer in private testnet for inte…
randomshinichi Apr 10, 2019
2252a7c
chore: AENS workflow integration test
randomshinichi Apr 11, 2019
10e5acf
refactor: NamePointer is now a struct of its own
randomshinichi Apr 17, 2019
3586ac6
refactor: NamePointer struct now embeds models.NamePointer. This give…
randomshinichi Apr 17, 2019
7d059b6
fix: canonical NameUpdateTx RLP serialization seems to want NamePoint…
randomshinichi Apr 17, 2019
62d8e6a
docs: added comment for NamePointer.EncodeRLP()
randomshinichi Apr 17, 2019
a3c1e7d
chore: AENS integration test generalize to name as a variable, do not…
randomshinichi Apr 18, 2019
d492629
fix: GenericTx was not deserializing properly into go-swagger models.
randomshinichi Apr 18, 2019
0059263
fix: unittest for GenericTx deserialization
randomshinichi Apr 18, 2019
094a0b2
chore: cleanup obsolete code
randomshinichi Apr 18, 2019
86e0321
docs: TODO consider refactoring WaitForTransactionUntilHeight
randomshinichi Apr 18, 2019
8746661
feat: AENS integration test now tests NamePreclaim, NameClaim, NameUp…
randomshinichi Apr 23, 2019
16fe029
feat: vanity account search takes regular expressions that can match …
randomshinichi Apr 23, 2019
b3adf6f
feat: account address will ask for confirmation before printing priva…
randomshinichi Apr 23, 2019
f8553d3
fix: generated.models.Error is patched to satisfy the Error interface…
randomshinichi Apr 23, 2019
7840b0d
chore: small unittest to test that generated models.Error has its Rea…
randomshinichi Apr 24, 2019
12829db
feat: Oracle helper functions for OracleRegisterTx, OracleExtendTx
randomshinichi Apr 24, 2019
7d5242c
refactor: Tx interface does not include JSON() method. This was more …
randomshinichi Apr 24, 2019
dc8c6ca
chore: Oracle Integration Test, OracleRegister, OracleExtend
randomshinichi Apr 24, 2019
ea3f833
feat: OracleQueryTx works. Unittest reference value taken from node, …
randomshinichi Apr 25, 2019
abc1e9a
feat: OracleRespondTx prerequisites: APIGetOracleQueriesByPubkey(), b…
randomshinichi Apr 25, 2019
5d1677c
feat: OracleRespondTx struct, RLP() and JSON() serialization and unit…
randomshinichi Apr 25, 2019
10e3755
feat: OracleRespondTx integration test
randomshinichi Apr 25, 2019
6675fad
chore: cleanup - comments and casing
randomshinichi Apr 26, 2019
2badad8
chore: standardize field names across transactions
randomshinichi Apr 26, 2019
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
17 changes: 17 additions & 0 deletions aeternity/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,20 @@ func getOracleByPubkey(node *apiclient.Node, pubkey string) (oracle *models.Regi
oracle = r.Payload
return
}

// APIGetOracleQueriesByPubkey get a list of queries made to a particular oracle
func (ae *Ae) APIGetOracleQueriesByPubkey(pubkey string) (oracleQueries *models.OracleQueries, err error) {
return getOracleQueriesByPubkey(ae.Node, pubkey)
}

// getOracleQueriesByPubkey get a list of queries made to a particular oracle
func getOracleQueriesByPubkey(node *apiclient.Node, pubkey string) (oracleQueries *models.OracleQueries, err error) {
p := external.NewGetOracleQueriesByPubkeyParams().WithPubkey(pubkey)
r, err := node.External.GetOracleQueriesByPubkey(p)
if err != nil {
err = fmt.Errorf("Error: %v", getErrorReason(err))
return
}
oracleQueries = r.Payload
return
}
34 changes: 34 additions & 0 deletions aeternity/hashing.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,39 @@ func hash(in []byte) (out []byte, err error) {
return
}

func leftPadByteSlice(length int, data []byte) []byte {
dataLen := len(data)
t := make([]byte, length-dataLen)
paddedSlice := append(t, data...)
return paddedSlice
}

func buildOracleQueryID(sender string, senderNonce uint64, recipient string) (id string, err error) {
queryIDBin := []byte{}
senderBin, err := Decode(sender)
if err != nil {
return
}
queryIDBin = append(queryIDBin, senderBin...)

senderNonceBytes := utils.NewBigIntFromUint64(senderNonce).Bytes()
senderNonceBytesPadded := leftPadByteSlice(32, senderNonceBytes)
queryIDBin = append(queryIDBin, senderNonceBytesPadded...)

recipientBin, err := Decode(recipient)
if err != nil {
return
}
queryIDBin = append(queryIDBin, recipientBin...)

hashedQueryID, err := hash(queryIDBin)
if err != nil {
return
}
id = Encode(PrefixOracleQueryID, hashedQueryID)
return
}

// Namehash calculate the Namehash of a string
// TODO: link to the
func Namehash(name string) []byte {
Expand Down Expand Up @@ -157,6 +190,7 @@ func buildIDTag(IDTag uint8, encodedHash string) (v []uint8, err error) {
return
}

// DecodeRLPMessage transforms a plain stream of bytes into a structure of bytes that represents the object that was serialized
func DecodeRLPMessage(rawBytes []byte) []interface{} {
res := []interface{}{}
rlp.DecodeBytes(rawBytes, &res)
Expand Down
85 changes: 85 additions & 0 deletions aeternity/hashing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,88 @@ func Test_computeCommitmentID(t *testing.T) {
})
}
}

func Test_buildOracleQueryID(t *testing.T) {
type args struct {
sender string
senderNonce uint64
recipient string
}
tests := []struct {
name string
args args
wantID string
wantErr bool
}{
{
name: "a simple oracle query id",
args: args{
sender: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
senderNonce: uint64(3),
recipient: "ok_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
},
wantID: "oq_2NhMjBdKHJYnQjDbAxanmxoXiSiWDoG9bqDgk2MfK2X6AB9Bwx",
wantErr: false,
},
{
name: "this test case copied from aepp-middleware",
args: args{
sender: "ak_2ZjpYpJbzq8xbzjgPuEpdq9ahZE7iJRcAYC1weq3xdrNbzRiP4",
senderNonce: uint64(1),
recipient: "ok_2iqfJjbhGgJFRezjX6Q6DrvokkTM5niGEHBEJZ7uAG5fSGJAw1",
},
wantID: "oq_2YvZnoohcSvbQCsPKSMxc98i5HZ1sU5mR6xwJUZC3SvkuSynMj",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotID, err := buildOracleQueryID(tt.args.sender, tt.args.senderNonce, tt.args.recipient)
if (err != nil) != tt.wantErr {
t.Errorf("buildOracleQueryID() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotID != tt.wantID {
gotIDBytes, _ := Decode(gotID)
wantIDBytes, _ := Decode(tt.wantID)
t.Errorf("buildOracleQueryID() = \n%v\n%v, want \n%v\n%v", gotID, gotIDBytes, tt.wantID, wantIDBytes)
}
})
}
}

func Test_leftPadByteSlice(t *testing.T) {
type args struct {
length int
data []byte
}
tests := []struct {
name string
args args
want []byte
}{
{
name: "Left pad a nonce of 3 to 32 bytes",
args: args{
length: 32,
data: []byte{3},
},
want: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3},
},
{
name: "Left pad a multi-byte value to 32 bytes",
args: args{
length: 32,
data: []byte{1, 2, 3, 4, 3},
},
want: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 3},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := leftPadByteSlice(tt.args.length, tt.args.data); !reflect.DeepEqual(got, tt.want) {
t.Errorf("leftPadByteSlice() = %v, want %v", got, tt.want)
}
})
}
}
199 changes: 111 additions & 88 deletions aeternity/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,6 @@ func urlComponents(url string) (host string, schemas []string) {
return
}

// GetTTL returns the chain height + offset
func (ae *Ae) GetTTL(offset uint64) (height uint64, err error) {
return getTTL(ae.Node, offset)
}

// GetNextNonce retrieves the current accountNonce for an account + 1
func (ae *Ae) GetNextNonce(accountID string) (nextNonce uint64, err error) {
return getNextNonce(ae.Node, accountID)
}

// GetTTLNonce is a convenience function that combines GetTTL() and GetNextNonce()
func (ae *Ae) GetTTLNonce(accountID string, offset uint64) (txTTL, accountNonce uint64, err error) {
return getTTLNonce(ae.Node, accountID, offset)
}

func getTTL(node *apiclient.Node, offset uint64) (height uint64, err error) {
kb, err := getTopBlock(node)
if err != nil {
Expand Down Expand Up @@ -104,18 +89,6 @@ func waitForTransaction(nodeClient *apiclient.Node, txHash string) (blockHeight
return
}

// // SpendTxStr creates an unsigned SpendTx but returns the base64 representation instead of an RLP bytestring
// func SpendTxStr(sender, recipient string, amount, fee utils.BigInt, message string, txTTL, accountNonce uint64) (base64Tx string, err error) {
// rlpUnsignedTx, err := NewSpendTx(sender, recipient, amount, fee, message, txTTL, accountNonce)
// if err != nil {
// return
// }

// base64Tx = Encode(PrefixTransaction, rlpUnsignedTx)

// return base64Tx, err
// }

// BroadcastTransaction recalculates the transaction hash and sends the transaction to the node.
func (ae *Ae) BroadcastTransaction(txSignedBase64 string) (err error) {
// Get back to RLP to calculate txhash
Expand All @@ -131,71 +104,16 @@ func (ae *Ae) BroadcastTransaction(txSignedBase64 string) (err error) {
return
}

// NamePreclaimTx creates a name preclaim transaction and salt (required for claiming)
// It should return the Tx struct, not the base64 encoded RLP, to ease subsequent inspection.
func (n *Aens) NamePreclaimTx(name string, fee utils.BigInt) (tx NamePreclaimTx, nameSalt *utils.BigInt, err error) {
txTTL, accountNonce, err := getTTLNonce(n.nodeClient, n.owner.Address, Config.Client.TTL)
if err != nil {
return
}

// calculate the commitment and get the preclaim salt
// since the salt is 32 bytes long, you must use a big.Int to convert it into an integer
cm, nameSalt, err := generateCommitmentID(name)
if err != nil {
return NamePreclaimTx{}, utils.NewBigInt(), err
}

// build the transaction
tx = NewNamePreclaimTx(n.owner.Address, cm, fee, txTTL, accountNonce)
if err != nil {
return NamePreclaimTx{}, utils.NewBigInt(), err
}

return
}

// NameClaimTx creates a claim transaction
func (n *Aens) NameClaimTx(name string, nameSalt utils.BigInt, fee utils.BigInt) (tx NameClaimTx, err error) {
txTTL, accountNonce, err := getTTLNonce(n.nodeClient, n.owner.Address, Config.Client.TTL)
if err != nil {
return
}

// create the transaction
tx = NewNameClaimTx(n.owner.Address, name, nameSalt, fee, txTTL, accountNonce)

return tx, err
// GetTTL returns the chain height + offset
func (ae *Ae) GetTTL(offset uint64) (height uint64, err error) {
return getTTL(ae.Node, offset)
}

// NameUpdateTx perform a name update
func (n *Aens) NameUpdateTx(name string, targetAddress string) (tx NameUpdateTx, err error) {
txTTL, accountNonce, err := getTTLNonce(n.nodeClient, n.owner.Address, Config.Client.TTL)
if err != nil {
return
}

encodedNameHash := Encode(PrefixName, Namehash(name))
absNameTTL, err := getTTL(n.nodeClient, Config.Client.Names.NameTTL)
if err != nil {
return NameUpdateTx{}, err
}
// create and sign the transaction
tx = NewNameUpdateTx(n.owner.Address, encodedNameHash, []string{targetAddress}, absNameTTL, Config.Client.Names.ClientTTL, Config.Client.Names.UpdateFee, txTTL, accountNonce)

return
// GetNextNonce retrieves the current accountNonce for an account + 1
func (ae *Ae) GetNextNonce(accountID string) (nextNonce uint64, err error) {
return getNextNonce(ae.Node, accountID)
}

// // OracleRegisterTxStr register an oracle
// func (o *Oracle) OracleRegisterTxStr(accountNonce uint64, querySpec, responseSpec string, queryFee utils.BigInt, oracleTTLType, oracleTTLValue, abiVersion uint64, txFee utils.BigInt, txTTL uint64) (tx string, err error) {
// txRaw, err := OracleRegisterTx(o.owner.Address, accountNonce, querySpec, responseSpec, Config.Client.Oracles.QueryFee, oracleTTLType, oracleTTLValue, Config.Client.Oracles.VMVersion, txFee, txTTL)
// if err != nil {
// return "", err
// }
// tx = Encode(PrefixTransaction, txRaw)
// return
// }

// PrintGenerationByHeight utility function to print a generation by it's height
func (ae *Ae) PrintGenerationByHeight(height uint64) {
p := external.NewGetGenerationByHeightParams().WithHeight(height)
Expand Down Expand Up @@ -261,6 +179,7 @@ Main:
p := external.NewGetMicroBlockTransactionsByHashParams().WithHash(mbhs)
r, mErr := ae.External.GetMicroBlockTransactionsByHash(p)
if mErr != nil {
// TODO: err will still be nil outside this scope. Consider refactoring whole function.
err = mErr
break Main
}
Expand Down Expand Up @@ -293,6 +212,110 @@ Main:
return
}

// GetTTLNonce is a convenience function that combines GetTTL() and GetNextNonce()
func (ae *Ae) GetTTLNonce(accountID string, offset uint64) (txTTL, accountNonce uint64, err error) {
return getTTLNonce(ae.Node, accountID, offset)
}

// NamePreclaimTx creates a name preclaim transaction and salt (required for claiming)
// It should return the Tx struct, not the base64 encoded RLP, to ease subsequent inspection.
func (n *Aens) NamePreclaimTx(name string, fee utils.BigInt) (tx NamePreclaimTx, nameSalt *utils.BigInt, err error) {
txTTL, accountNonce, err := getTTLNonce(n.nodeClient, n.owner.Address, Config.Client.TTL)
if err != nil {
return
}

// calculate the commitment and get the preclaim salt
// since the salt is 32 bytes long, you must use a big.Int to convert it into an integer
cm, nameSalt, err := generateCommitmentID(name)
if err != nil {
return NamePreclaimTx{}, utils.NewBigInt(), err
}

// build the transaction
tx = NewNamePreclaimTx(n.owner.Address, cm, fee, txTTL, accountNonce)
if err != nil {
return NamePreclaimTx{}, utils.NewBigInt(), err
}

return
}

// NameClaimTx creates a claim transaction
func (n *Aens) NameClaimTx(name string, nameSalt utils.BigInt, fee utils.BigInt) (tx NameClaimTx, err error) {
txTTL, accountNonce, err := getTTLNonce(n.nodeClient, n.owner.Address, Config.Client.TTL)
if err != nil {
return
}

// create the transaction
tx = NewNameClaimTx(n.owner.Address, name, nameSalt, fee, txTTL, accountNonce)

return tx, err
}

// NameUpdateTx perform a name update
func (n *Aens) NameUpdateTx(name string, targetAddress string) (tx NameUpdateTx, err error) {
txTTL, accountNonce, err := getTTLNonce(n.nodeClient, n.owner.Address, Config.Client.TTL)
if err != nil {
return
}

encodedNameHash := Encode(PrefixName, Namehash(name))
absNameTTL, err := getTTL(n.nodeClient, Config.Client.Names.NameTTL)
if err != nil {
return NameUpdateTx{}, err
}
// create and sign the transaction
tx = NewNameUpdateTx(n.owner.Address, encodedNameHash, []string{targetAddress}, absNameTTL, Config.Client.Names.ClientTTL, Config.Client.Names.UpdateFee, txTTL, accountNonce)

return
}

// OracleRegisterTx create a new oracle
func (o *Oracle) OracleRegisterTx(querySpec, responseSpec string, queryFee utils.BigInt, oracleTTLType, oracleTTLValue, abiVersion uint64, vmVersion uint64) (tx OracleRegisterTx, err error) {
ttl, nonce, err := getTTLNonce(o.nodeClient, o.owner.Address, Config.Client.TTL)
if err != nil {
return OracleRegisterTx{}, err
}

tx = NewOracleRegisterTx(o.owner.Address, nonce, querySpec, responseSpec, queryFee, oracleTTLType, oracleTTLValue, abiVersion, vmVersion, Config.Client.Fee, ttl)
return tx, nil
}

// OracleExtendTx extend the lifetime of an existing oracle
func (o *Oracle) OracleExtendTx(oracleID string, ttlType, ttlValue uint64) (tx OracleExtendTx, err error) {
ttl, nonce, err := getTTLNonce(o.nodeClient, o.owner.Address, Config.Client.TTL)
if err != nil {
return OracleExtendTx{}, err
}

tx = NewOracleExtendTx(oracleID, nonce, ttlType, ttlValue, Config.Client.Fee, ttl)
return tx, nil
}

// OracleQueryTx ask something of an oracle
func (o *Oracle) OracleQueryTx(OracleID, Query string, QueryFee utils.BigInt, QueryTTLType, QueryTTLValue, ResponseTTLType, ResponseTTLValue uint64) (tx OracleQueryTx, err error) {
ttl, nonce, err := getTTLNonce(o.nodeClient, o.owner.Address, Config.Client.TTL)
if err != nil {
return OracleQueryTx{}, err
}

tx = NewOracleQueryTx(o.owner.Address, nonce, OracleID, Query, QueryFee, QueryTTLType, QueryTTLValue, ResponseTTLType, ResponseTTLValue, Config.Client.Fee, ttl)
return tx, nil
}

// OracleRespondTx the oracle responds by sending this transaction
func (o *Oracle) OracleRespondTx(OracleID string, QueryID string, Response string, TTLType uint64, TTLValue uint64) (tx OracleRespondTx, err error) {
ttl, nonce, err := getTTLNonce(o.nodeClient, o.owner.Address, Config.Client.TTL)
if err != nil {
return OracleRespondTx{}, err
}

tx = NewOracleRespondTx(OracleID, nonce, QueryID, Response, TTLType, TTLValue, Config.Client.Fee, ttl)
return tx, nil
}

// StoreAccountToKeyStoreFile store an account to a json file
func StoreAccountToKeyStoreFile(account *Account, password, walletName string) (filePath string, err error) {
// keystore will be saved in current directory
Expand Down
Loading