V1 Resolution example (go)

The following Go code and example command-line application demonstrates how to efficiently resolve the smart-signature account address for a name, or an address on-chain.

This code is only for NFDs registered prior to the deployment of the 2.x NFD registry.

/*
 * Copyright (c) 2022. TxnLab Inc.
 * All Rights reserved.
 */

package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"log"
	"reflect"

	"github.com/algorand/go-algorand-sdk/crypto"
	"github.com/algorand/go-algorand-sdk/types"
)

func main() {
	var (
		lsig     crypto.LogicSigAccount
		lsigAddr types.Address
		err      error
	)
	name := flag.String("name", "", ".Algo Name for forward lookup - invalid names can be passed here but would never be allowed to be minted...")
	address := flag.String("addr", "", "Algorand address for reverse-address lookup")
	regAppID := flag.Uint64("id", 760937186, "Registry application id (mainnet defaulted)")
	flag.Parse()

	if *name == "" && *address == "" {
		flag.Usage()
		log.Fatalln("You must specify a name, or an address")
	}
	if *name != "" {
		lsig, err = GetNFDSigNameLSIG(*name, *regAppID)
	}
	if *address != "" {
		addr, err := types.DecodeAddress(*address)
		if err != nil {
			log.Fatalln("Error decoding algoand address parameter:", err)
		}
		lsig, err = GetNFDSigRevAddressLSIG(addr, *regAppID)
	}
	if err != nil {
		log.Fatalln("error in lsig calculation:", err)
	}
	lsigAddr, err = lsig.Address()
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println("Registration account:", lsigAddr.String())
}

func getLookupLSIG(prefixBytes, lookupBytes string, registryAppID uint64) (crypto.LogicSigAccount, error) {
	/*
		#pragma version 5
		intcblock 1
		pushbytes 0x0102030405060708
		btoi
		store 0
		txn ApplicationID
		load 0
		==
		txn TypeEnum
		pushint 6
		==
		&&
		txn OnCompletion
		intc_0 // 1
		==
		txn OnCompletion
		pushint 0
		==
		||
		&&
		bnz label1
		err
		label1:
		intc_0 // 1
		return
		bytecblock "xxx"
	*/
	sigLookupByteCode := []byte{
		0x05, 0x20, 0x01, 0x01, 0x80, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
		0x07, 0x08, 0x17, 0x35, 0x00, 0x31, 0x18, 0x34, 0x00, 0x12, 0x31, 0x10,
		0x81, 0x06, 0x12, 0x10, 0x31, 0x19, 0x22, 0x12, 0x31, 0x19, 0x81, 0x00,
		0x12, 0x11, 0x10, 0x40, 0x00, 0x01, 0x00, 0x22, 0x43, 0x26, 0x01,
	}
	contractSlice := sigLookupByteCode[6:14]
	if !reflect.DeepEqual(contractSlice, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}) {
		return crypto.LogicSigAccount{}, errors.New("Lookup template doesn't match expectation")
	}
	// Bytes 6-13 [0-index] with 0x01-0x08 placeholders is where we put the Registry Contract App ID bytes in big-endian
	binary.BigEndian.PutUint64(contractSlice, registryAppID)

	// We then 'append' the bytes of the prefix + lookup to the end in a bytecblock chunk
	// ie: name/patrick.algo, or address/RXZRFW26WYHFV44APFAK4BEMU3P54OBK47LCAZQJPXOTZ4AZPSFDAKLIQY
	// - the 0x26 0x01 at end of sigLookupByteCode is the bytecblock opcode and specifying a single value is being added

	// We write the uvarint length of our lookup bytes.. then append the bytes of that lookpup string..
	bytesToAppend := bytes.Join([][]byte{[]byte(prefixBytes), []byte(lookupBytes)}, nil)
	uvarIntBytes := make([]byte, binary.MaxVarintLen64)
	nBytes := binary.PutUvarint(uvarIntBytes, uint64(len(bytesToAppend)))
	composedBytecode := bytes.Join([][]byte{sigLookupByteCode, uvarIntBytes[:nBytes], bytesToAppend}, nil)

	logicSig := crypto.MakeLogicSigAccountEscrow(composedBytecode, [][]byte{})
	return logicSig, nil
}

func GetNFDSigNameLSIG(nfdName string, registryAppID uint64) (crypto.LogicSigAccount, error) {
	return getLookupLSIG("name/", nfdName, registryAppID)
}

func GetNFDSigRevAddressLSIG(pointedToAddress types.Address, registryAppID uint64) (crypto.LogicSigAccount, error) {
	return getLookupLSIG("address/", pointedToAddress.String(), registryAppID)
}

To be clear, this "lookup" is a hash to get to an Algorand account that may contain state. It does not mean data exists there at the moment.

Last updated