feat: use ens API

This commit is contained in:
Anthony Laibe 2021-12-22 14:41:13 +01:00 committed by Anthony Laibe
parent 1049b37c49
commit b507813587
6 changed files with 85 additions and 271 deletions

View File

@ -15,6 +15,8 @@ import ./statusgo_backend/eth as eth
import ./statusgo_backend/wallet
import ./statusgo_backend/accounts as status_accounts
import ./statusgo_backend/settings as status_settings
import ./statusgo_backend_new/ens as status_ens
import ./types/[transaction, setting, rpc_response, network_type, network, profile]
import ./utils
import ./transactions
@ -49,6 +51,45 @@ proc userNameOrAlias*(contact: Profile, removeSuffix: bool = false): string =
else:
result = contact.alias
proc resolver*(username: string): string =
let chainId = status_settings.getCurrentNetwork().toChainId()
let res = status_ens.resolver(chainId, username)
return res.result.getStr
proc owner*(username: string): string =
let chainId = status_settings.getCurrentNetwork().toChainId()
let res = status_ens.ownerOf(chainId, username)
let address = res.result.getStr
if address == "0x0000000000000000000000000000000000000000":
return ""
return address
proc pubkey*(username: string): string =
try:
let chainId = status_settings.getCurrentNetwork().toChainId()
let res = status_ens.publicKeyOf(chainId, addDomain(username))
var key = res.result.getStr
key.removePrefix("0x")
return "0x04" & key
except:
return ""
proc address*(username: string): string =
let chainId = status_settings.getCurrentNetwork().toChainId()
let res = status_ens.addressOf(chainId, username)
return res.result.getStr
proc contenthash*(username: string): string =
let chainId = status_settings.getCurrentNetwork().toChainId()
let res = status_ens.contentHash(chainId, username)
return res.result.getStr
proc getPrice*(): Stuint[256] =
let chainId = status_settings.getCurrentNetwork().toChainId()
let res = status_ens.price(chainId)
return fromHex(Stuint[256], res.result.getStr)
proc label*(username:string): string =
var node:array[32, byte] = keccak_256.digest(username.toLower()).data
result = "0x" & node.toHex()
@ -68,110 +109,6 @@ proc namehash*(ensName:string): string =
result = "0x" & node.toHex()
const registry* = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
const resolver_signature = "0x0178b8bf"
proc resolver*(usernameHash: string): string =
let payload = %* [{
"to": registry,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{resolver_signature}{userNameHash}"
}, "latest"]
let response = eth.call(payload)
# TODO: error handling
var resolverAddr = response.result
resolverAddr.removePrefix("0x000000000000000000000000")
result = "0x" & resolverAddr
const owner_signature = "0x02571be3" # owner(bytes32 node)
proc owner*(username: string): string =
var userNameHash = namehash(addDomain(username))
userNameHash.removePrefix("0x")
let payload = %* [{
"to": registry,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{owner_signature}{userNameHash}"
}, "latest"]
let response = eth.call(payload)
# TODO: error handling
let ownerAddr = response.result
if ownerAddr == "0x0000000000000000000000000000000000000000000000000000000000000000":
return ""
result = "0x" & ownerAddr.substr(26)
const pubkey_signature = "0xc8690233" # pubkey(bytes32 node)
proc pubkey*(username: string): string =
var userNameHash = namehash(addDomain(username))
userNameHash.removePrefix("0x")
let ensResolver = resolver(userNameHash)
let payload = %* [{
"to": ensResolver,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{pubkey_signature}{userNameHash}"
}, "latest"]
let response = eth.call(payload)
# TODO: error handling
var pubkey = response.result
if pubkey == "0x" or pubkey == "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000":
result = ""
else:
pubkey.removePrefix("0x")
result = "0x04" & pubkey
const address_signature = "0x3b3b57de" # addr(bytes32 node)
proc address*(username: string): string =
var userNameHash = namehash(addDomain(username))
userNameHash.removePrefix("0x")
let ensResolver = resolver(userNameHash)
let payload = %* [{
"to": ensResolver,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{address_signature}{userNameHash}"
}, "latest"]
let response = eth.call(payload)
# TODO: error handling
let address = response.result
if address == "0x0000000000000000000000000000000000000000000000000000000000000000":
return ""
result = "0x" & address.substr(26)
const contenthash_signature = "0xbc1c58d1" # contenthash(bytes32)
proc contenthash*(ensAddr: string): string =
var ensHash = namehash(ensAddr)
ensHash.removePrefix("0x")
let ensResolver = resolver(ensHash)
let payload = %* [{
"to": ensResolver,
"from": "0x0000000000000000000000000000000000000000",
"data": fmt"{contenthash_signature}{ensHash}"
}, "latest"]
let response = eth.call(payload)
let bytesResponse = response.result
if bytesResponse == "0x":
return ""
let size = fromHex(Stuint[256], bytesResponse[66..129]).truncate(int)
result = bytesResponse[130..129+size*2]
result = bytesResponse[130..129+size*2]
proc getPrice*(): Stuint[256] =
let
network = status_settings.getCurrentNetwork().toNetwork()
contract = contracts.findContract(network.chainId, "ens-usernames")
payload = %* [{
"to": $contract.address,
"data": contract.methods["getPrice"].encodeAbi()
}, "latest"]
let response = eth.call(payload)
if not response.error.isNil:
raise newException(RpcException, "Error getting ens username price: " & response.error.message)
if response.result == "0x":
raise newException(RpcException, "Error getting ens username price: 0x")
result = fromHex(Stuint[256], response.result)
result = fromHex(Stuint[256], response.result)
proc releaseEstimateGas*(username: string, address: string, success: var bool): int =
let
label = fromHex(FixedBytes[32], label(username))
@ -184,8 +121,7 @@ proc releaseEstimateGas*(username: string, address: string, success: var bool):
let response = ensUsernamesContract.methods["release"].estimateGas(tx, release, success)
if success:
result = fromHex[int](response)
except RpcException as e:
error "Could not estimate gas for ens release", err=e.msg
except rpc_response.RpcException as e:
error "Could not estimate gas for ens release", err=e.msg
proc release*(username: string, address: string, gas, gasPrice, password: string, success: var bool): string =
@ -200,30 +136,13 @@ proc release*(username: string, address: string, gas, gasPrice, password: strin
result = ensUsernamesContract.methods["release"].send(tx, release, password, success)
if success:
trackPendingTransaction(result, address, $ensUsernamesContract.address, PendingTransactionType.ReleaseENS, username)
except RpcException as e:
except rpc_response.RpcException as e:
error "Could not estimate gas for ens release", err=e.msg
proc getExpirationTime*(username: string, success: var bool): int =
let
label = fromHex(FixedBytes[32], label(username))
expTime = ExpirationTime(label: label)
network = status_settings.getCurrentNetwork().toNetwork()
ensUsernamesContract = contracts.findContract(network.chainId, "ens-usernames")
var tx = transactions.buildTransaction(parseAddress("0x0000000000000000000000000000000000000000"), 0.u256)
tx.to = ensUsernamesContract.address.some
tx.data = ensUsernamesContract.methods["getExpirationTime"].encodeAbi(expTime)
var response = ""
try:
response = eth.call(tx).result
success = true
except RpcException as e:
success = false
error "Error obtaining expiration time", err=e.msg
if success:
result = fromHex[int](response)
let chainId = status_settings.getCurrentNetwork().toChainId()
let res = status_ens.expireAt(chainId, username)
return fromHex[int](res.result.getStr)
proc extractCoordinates*(pubkey: string):tuple[x: string, y:string] =
result = ("0x" & pubkey[4..67], "0x" & pubkey[68..131])
@ -277,7 +196,6 @@ proc setPubKeyEstimateGas*(username: string, address: string, pubKey: string, su
var hash = namehash(username)
hash.removePrefix("0x")
let
label = fromHex(FixedBytes[32], "0x" & hash)
x = fromHex(FixedBytes[32], "0x" & pubkey[4..67])
@ -293,7 +211,7 @@ proc setPubKeyEstimateGas*(username: string, address: string, pubKey: string, su
let response = resolverContract.methods["setPubkey"].estimateGas(tx, setPubkey, success)
if success:
result = fromHex[int](response)
except RpcException as e:
except rpc_response.RpcException as e:
raise
proc setPubKey*(username, pubKey, address, gas, gasPrice: string, isEIP1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, success: var bool): string =
@ -315,7 +233,7 @@ proc setPubKey*(username, pubKey, address, gas, gasPrice: string, isEIP1559Enabl
result = resolverContract.methods["setPubkey"].send(tx, setPubkey, password, success)
if success:
trackPendingTransaction(result, $address, resolverAddress, PendingTransactionType.SetPubKey, username)
except RpcException as e:
except rpc_response.RpcException as e:
raise
proc statusRegistrarAddress*():string =
@ -325,54 +243,6 @@ proc statusRegistrarAddress*():string =
return $contract.address
result = ""
type
ENSType* {.pure.} = enum
IPFS,
SWARM,
IPNS,
UNKNOWN
proc decodeENSContentHash*(value: string): tuple[ensType: ENSType, output: string] =
if value == "":
return (ENSType.UNKNOWN, "")
if value[0..5] == "e40101":
return (ENSType.SWARM, value.split("1b20")[1])
if value[0..7] == "e3010170":
try:
let defaultCodec = parseHexInt("70") #dag-pb
var codec = defaultCodec # no codec specified
var codecStartIdx = 2 # idx of where codec would start if it was specified
# handle the case when starts with 0xe30170 instead of 0xe3010170
if value[2..5] == "0101":
codecStartIdx = 6
codec = parseHexInt(value[6..7])
elif value[2..3] == "01" and value[4..5] != "12":
codecStartIdx = 4
codec = parseHexInt(value[4..5])
# strip the info we no longer need
var multiHashStr = value[codecStartIdx + 2..<value.len]
# The rest of the hash identifies the multihash algo, length, and digest
# More info: https://multiformats.io/multihash/
# 12 = identifies sha2-256 hash
# 20 = multihash length = 32
# ...rest = multihash digest
let
multiHash = MultiHash.init(nimcrypto.fromHex(multiHashStr)).get()
decoded = Cid.init(CIDv0, MultiCodec.codec(codec), multiHash).get()
return (ENSType.IPFS, $decoded)
except Exception as e:
error "Error decoding ENS contenthash", hash=value, exception=e.msg
raise
if value[0..8] == "e50101700":
return (ENSType.IPNS, parseHexStr(value[12..value.len-1]))
return (ENSType.UNKNOWN, "")
proc validateEnsName*(ens: string, isStatus: bool, usernames: seq[string]): string =
var username = ens & (if(isStatus): domain else: "")
result = ""

View File

@ -1,57 +0,0 @@
import ens, wallet, permissions, utils
import ../eventemitter
import ./types/[setting, permission]
import utils
import statusgo_backend/accounts
import statusgo_backend/core
import statusgo_backend/settings as status_settings
import json, json_serialization, sets, strutils
import chronicles
import stew/byteutils
from stew/base32 import nil
from stew/base58 import nil
const HTTPS_SCHEME* = "https"
const IPFS_GATEWAY* = ".infura.status.im"
const SWARM_GATEWAY* = "swarm-gateways.net"
logScope:
topics = "provider-model"
type ProviderModel* = ref object
events*: EventEmitter
permissions*: PermissionsModel
wallet*: WalletModel
proc newProviderModel*(events: EventEmitter, permissions: PermissionsModel, wallet: WalletModel): ProviderModel =
result = ProviderModel()
result.events = events
result.permissions = permissions
result.wallet = wallet
proc ensResourceURL*(self: ProviderModel, ens: string, url: string):
(string, string, string, string, bool) =
let contentHash = contenthash(ens)
if contentHash == "": # ENS does not have a content hash
return (url, url, HTTPS_SCHEME, "", false)
let decodedHash = contentHash.decodeENSContentHash()
case decodedHash[0]:
of ENSType.IPFS:
let
base58bytes = base58.decode(base58.BTCBase58, decodedHash[1])
base32Hash = base32.encode(base32.Base32Lower, base58bytes)
result = (url, base32Hash & IPFS_GATEWAY, HTTPS_SCHEME, "", true)
of ENSType.SWARM:
result = (url, SWARM_GATEWAY, HTTPS_SCHEME,
"/bzz:/" & decodedHash[1] & "/", true)
of ENSType.IPNS:
result = (url, decodedHash[1], HTTPS_SCHEME, "", true)
else:
warn "Unknown content for", ens, contentHash

View File

@ -24,7 +24,6 @@ proc callPrivateRPC*(methodName: string, payload = %* []): RpcResponse[JsonNode]
debug "NewBE_callPrivateRPC", rpc_method=methodName
let rpcResponseRaw = status_go.callPrivateRPC($inputJSON)
result = Json.decode(rpcResponseRaw, RpcResponse[JsonNode])
if(not result.error.isNil):
var err = "\nstatus-go error ["
err &= fmt"methodName:{methodName}, "

View File

@ -3,20 +3,40 @@ import ./core, ./response_type
export response_type
proc resolver*(username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
# TODO: Use a real chain id
let payload = %* [1, username]
proc resolver*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, username]
return core.callPrivateRPC("ens_resolver", payload)
proc contentHash*(username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
# TODO: Use a real chain id
let payload = %* [1, username]
proc ownerOf*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, username]
return core.callPrivateRPC("ens_ownerOf", payload)
proc contentHash*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, username]
return core.callPrivateRPC("ens_contentHash", payload)
proc resourceURL*(username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
# TODO: Use a real chain id
let payload = %* [1, username]
return core.callPrivateRPC("ens_resourceURL", payload)
proc publicKeyOf*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, username]
return core.callPrivateRPC("ens_publicKeyOf", payload)
proc addressOf*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, username]
return core.callPrivateRPC("ens_addressOf", payload)
proc expireAt*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, username]
return core.callPrivateRPC("ens_expireAt", payload)
proc price*(chainId: int): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId]
return core.callPrivateRPC("ens_price", payload)
proc resourceURL*(chainId: int, username: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, username]
return core.callPrivateRPC("ens_resourceURL", payload)

View File

@ -1,9 +1,10 @@
import ens, provider
import stew/byteutils
from stew/base32 import nil
from stew/base58 import nil
import ./statusgo_backend_new/ens as status_ens
import chronicles, httpclient, net
import strutils
import json
import semver
import constants
@ -14,31 +15,12 @@ type
url*: string
proc getLatestVersion*(): VersionInfo =
let contentHash = contenthash(APP_UPDATES_ENS)
if contentHash == "":
let response = status_ens.resourceUrl(chainId=1, username=APP_UPDATES_ENS)
let host = response.result{"Host"}.getStr
if host == "":
raise newException(ValueError, "ENS does not have a content hash")
var url: string = ""
let decodedHash = contentHash.decodeENSContentHash()
case decodedHash[0]:
of ENSType.IPFS:
let
base58bytes = base58.decode(base58.BTCBase58, decodedHash[1])
base32Hash = base32.encode(base32.Base32Lower, base58bytes)
url = "https://" & base32Hash & IPFS_GATEWAY
of ENSType.SWARM:
url = "https://" & SWARM_GATEWAY & "/bzz:/" & decodedHash[1]
of ENSType.IPNS:
url = "https://" & decodedHash[1]
else:
warn "Unknown content for", contentHash
raise newException(ValueError, "Unknown content for " & contentHash)
let url = "https://" & host & response.result{"Path"}.getStr
# Read version from folder
let secureSSLContext = newContext()

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 5c6549360a2eeb1e6232591d9d13fbff99ca6b49
Subproject commit 5512e19d83c9693bbb043a8aa6b3386164213c46