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

Refactor tests + Add support for asset re-issuance #144

Closed
wants to merge 7 commits into from
Closed
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
59 changes: 32 additions & 27 deletions address/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ func FromConfidential(address string) (*ConfidentialAddress, error) {
return nil, err
}

addressType, err := DecodeType(address, *net)
addressType, err := DecodeType(address)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -414,8 +414,8 @@ func NetworkForAddress(address string) (*network.Network, error) {

//ToOutputScript creates a new script to pay a transaction output to a the
//specified address
func ToOutputScript(address string, net network.Network) ([]byte, error) {
addressType, err := DecodeType(address, net)
func ToOutputScript(address string) ([]byte, error) {
addressType, err := DecodeType(address)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -494,6 +494,7 @@ func ToOutputScript(address string, net network.Network) ([]byte, error) {
}
}

// GetScriptType returns the type of the given script (p2pkh, p2sh, etc.)
func GetScriptType(script []byte) int {
switch script[0] {
case txscript.OP_0:
Expand All @@ -511,26 +512,38 @@ func GetScriptType(script []byte) int {
}

//DecodeType returns address type
func DecodeType(address string, net network.Network) (int, error) {
if isBlech32(address, net) {
return decodeBlech32(address, net)
func DecodeType(address string) (int, error) {
net, err := NetworkForAddress(address)
if err != nil {
return -1, err
}

if isBlech32(address, *net) {
return decodeBlech32(address, *net)
}
if isBech32(address, net) {
return decodeBech32(address, net)
if isBech32(address, *net) {
return decodeBech32(address, *net)
}
return decodeBase58(address, net)
return decodeBase58(address, *net)
}

func isBlech32(address string, net network.Network) bool {
oneIndex := strings.LastIndexByte(address, '1')
if oneIndex > 1 {
prefix := address[:oneIndex]
if prefix == net.Blech32 {
return true
}
return false
// IsConfidential checks whether the given address is confidential
func IsConfidential(address string) (bool, error) {
addressType, err := DecodeType(address)
if err != nil {
return false, err
}
return false

isConfidential := (addressType == ConfidentialP2Pkh ||
addressType == ConfidentialP2Sh ||
addressType == ConfidentialP2Wpkh ||
addressType == ConfidentialP2Wsh)

return isConfidential, nil
}

func isBlech32(address string, net network.Network) bool {
return strings.HasPrefix(address, net.Blech32)
}

func decodeBlech32(address string, net network.Network) (int, error) {
Expand All @@ -549,15 +562,7 @@ func decodeBlech32(address string, net network.Network) (int, error) {
}

func isBech32(address string, net network.Network) bool {
oneIndex := strings.LastIndexByte(address, '1')
if oneIndex > 1 {
prefix := address[:oneIndex]
if prefix == net.Bech32 {
return true
}
return false
}
return false
return strings.HasPrefix(address, net.Bech32)
}

func decodeBech32(address string, net network.Network) (int, error) {
Expand Down
12 changes: 1 addition & 11 deletions address/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/vulpemventures/go-elements/network"
)

func TestBase58(t *testing.T) {
Expand Down Expand Up @@ -102,53 +101,44 @@ func TestConfidential(t *testing.T) {
func TestDecodeAddressType(t *testing.T) {
tests := []struct {
address string
network network.Network
expectedType int
}{
{
address: "Q9863Eah5byyxdBX8zghpooS2x4Ey8XZyc",
network: network.Liquid,
expectedType: P2Pkh,
},
{
address: "H5RCjtzndKyzFnVe41yg62T3WViWguyz4M",
network: network.Liquid,
expectedType: P2Sh,
},
{
address: "ex1qlg343tpldc4wvjxn3jdq2qs35r8j5yd5vqrmu3",
network: network.Liquid,
expectedType: P2Wpkh,
},
{
address: "ert1q2z45rh444qmeand48lq0wp3jatxs2nzh492ds9s5yscv2pplxwesajz7q3",
network: network.Regtest,
expectedType: P2Wsh,
},
{
address: "VTpuLYhJwE8CFm6h1A6DASCaJuRQqkBt6qGfbebSHAUxGXsJMo8wtRvLZYZSWWXt89jG55pCF4YfxMjh",
network: network.Liquid,
expectedType: ConfidentialP2Pkh,
},
{
address: "VJLDHFUbw8oPUcwzmf9jw4tZdN57rEfAusRmWy6knHAF2a4rLGenJz5WPVuyggVzQPHY6JjzKuw31B6e",
network: network.Liquid,
expectedType: ConfidentialP2Sh,
},
{
address: "lq1qqwrdmhm69vsq3qfym06tlyhfze9ltauay9tv4r34ueplfwtjx0q27dk2c4d3a9ms6wum04efclqph7dg4unwcmwmw4vnqreq3",
network: network.Liquid,
expectedType: ConfidentialP2Wpkh,
},
{
address: "lq1qq2akvug2el2rg6lt6aewh9rzy7dglf9ajdmrkknnwwl3jwxgfkh985x3lrzmrq2mc3c6aa85wgxxfm9v8r062qwq4ty579p54pn2q2hqnhgwv394ycf8",
network: network.Liquid,
expectedType: ConfidentialP2Wsh,
},
}

for _, tt := range tests {
addressType, err := DecodeType(tt.address, tt.network)
addressType, err := DecodeType(tt.address)
if err != nil {
t.Fatal(err)
}
Expand Down
8 changes: 4 additions & 4 deletions confidential/confidential.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,14 @@ func unblindIssuance(
if len(blindKeys) <= 1 {
return nil, errors.New("missing asset blind private key")
}
if in.Issuance == nil {
if !in.HasAnyIssuance() {
return nil, errors.New("missing input issuance")
}
if len(in.IssuanceRangeProof) <= 0 {
if !in.HasConfidentialIssuance() {
return nil, errors.New("missing asset range proof")
}

if len(in.Issuance.TokenAmount) > 0 {
if in.Issuance.HasTokenAmount() {
if len(in.InflationRangeProof) <= 0 {
return nil, errors.New("missing token range proof")
}
Expand All @@ -267,7 +267,7 @@ func unblindIssuance(
Script: make([]byte, 0),
},
}
if len(in.Issuance.TokenAmount) > 0 {
if in.Issuance.HasTokenAmount() {
token, err := calcTokenHash(in)
if err != nil {
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ The input we just funded from the faucet and three outputs.

One for the ammount we want to send, one for the change and a last one for the fee.
txInputHash, _ := hex.DecodeString(utxos[0]["txid"].(string))
txInputHash = bufferutil.ReverseBytes(txInputHash)
txInputHash = elementsutil.ReverseBytes(txInputHash)
txInputIndex := uint32(utxos[0]["vout"].(float64))
txInput := transaction.NewTxInput(txInputHash, txInputIndex)

lbtc, _ := hex.DecodeString(
"5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225",
)
lbtc = append([]byte{0x01}, bufferutil.ReverseBytes(lbtc)...)
lbtc = append([]byte{0x01}, elementsutil.ReverseBytes(lbtc)...)
receiverValue, _ := confidential.SatoshiToElementsValue(60000000)
receiverScript, _ := hex.DecodeString("76a91439397080b51ef22c59bd7469afacffbeec0da12e88ac")
receiverOutput := transaction.NewTxOutput(lbtc, receiverValue[:], receiverScript)
Expand Down
18 changes: 16 additions & 2 deletions internal/elementsutil/amount.go → elementsutil/amount.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func SatoshiToElementsValue(val uint64) ([]byte, error) {
if err := bufferutil.BinarySerializer.PutUint64(b, binary.LittleEndian, val); err != nil {
return nil, err
}
res := append([]byte{unconfPrefix}, bufferutil.ReverseBytes(b.Bytes())...)
res := append([]byte{unconfPrefix}, ReverseBytes(b.Bytes())...)
return res, nil
}

Expand All @@ -27,7 +27,21 @@ func ElementsToSatoshiValue(val []byte) (uint64, error) {
if val[0] != byte(1) {
return 0, errors.New("invalid prefix")
}
reverseValueBuffer := bufferutil.ReverseBytes(val[1:])
reverseValueBuffer := ReverseBytes(val[1:])
d := bufferutil.NewDeserializer(bytes.NewBuffer(reverseValueBuffer))
return d.ReadUint64()
}

// ReverseBytes returns a copy of the given byte slice with elems in reverse order.
func ReverseBytes(buf []byte) []byte {
if len(buf) < 1 {
return buf
}
tmp := make([]byte, len(buf))
copy(tmp, buf)
for i := len(tmp)/2 - 1; i >= 0; i-- {
j := len(tmp) - 1 - i
tmp[i], tmp[j] = tmp[j], tmp[i]
}
return tmp
}
File renamed without changes.
14 changes: 0 additions & 14 deletions internal/bufferutil/bufferutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,17 +289,3 @@ func VarIntSerializeSize(val uint64) int {
func VarSliceSerializeSize(val []byte) int {
return VarIntSerializeSize(uint64(len(val))) + len(val)
}

// ReverseBytes returns a copy of the given byte slice with elems in reverse order.
func ReverseBytes(buf []byte) []byte {
if len(buf) < 1 {
return buf
}
tmp := make([]byte, len(buf))
copy(tmp, buf)
for i := len(tmp)/2 - 1; i >= 0; i-- {
j := len(tmp) - 1 - i
tmp[i], tmp[j] = tmp[j], tmp[i]
}
return tmp
}
49 changes: 25 additions & 24 deletions pset/blinder.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

"github.com/btcsuite/btcd/btcec"
"github.com/vulpemventures/go-elements/confidential"
"github.com/vulpemventures/go-elements/internal/elementsutil"
"github.com/vulpemventures/go-elements/elementsutil"
"github.com/vulpemventures/go-elements/transaction"
)

Expand Down Expand Up @@ -180,9 +180,12 @@ func (b *blinder) unblindInputs() (

// if the current input contains an issuance, add the pseudo input to the
// returned unblindedPseudoIns array
if input.HasIssuance() {
issuance := transaction.NewTxIssuanceFromContractHash(input.Issuance.AssetEntropy)
issuance.GenerateEntropy(input.Hash, input.Index)
if input.HasAnyIssuance() {
issuance, err := transaction.NewTxIssuanceFromInput(input)
if err != nil {
return nil, nil, err
}

asset, err := issuance.GenerateAsset()
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -211,9 +214,10 @@ func (b *blinder) unblindInputs() (

// if the token amount is not defined, it is set to 0x00, thus we need
// to check if the input.Issuance.TokenAmount, that is encoded in the
// elements format, contains more than one byte
if len(input.Issuance.TokenAmount) > 1 {
value, err := elementsutil.ElementsToSatoshiValue(input.Issuance.TokenAmount)
// elements format, contains more than one byte. We simply ignore the
// token amount for reissuances.
if i := input.Issuance; !i.IsReissuance() && i.HasTokenAmount() {
value, err := elementsutil.ElementsToSatoshiValue(i.TokenAmount)
if err != nil {
return nil, nil, err
}
Expand All @@ -225,9 +229,7 @@ func (b *blinder) unblindInputs() (
tokenFlag = 0
}

token, err := issuance.GenerateReissuanceToken(
tokenFlag,
)
token, err := issuance.GenerateReissuanceToken(tokenFlag)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -320,23 +322,20 @@ func (b *blinder) blindInputs(unblinded []confidential.UnblindOutputResult) erro

getBlindingFactors := func(asset []byte) ([]byte, []byte, error) {
for _, u := range unblinded {
if bytes.Compare(asset, u.Asset) == 0 {
if bytes.Equal(asset, u.Asset) {
return u.ValueBlindingFactor, u.AssetBlindingFactor, nil
}
}
return nil, nil, errors.New("no blinding factors generated for pseudo issuance inputs")
}

for index, input := range b.pset.UnsignedTx.Inputs {
if input.HasIssuance() {
issuance := transaction.NewTxIssuanceFromContractHash(
input.Issuance.AssetEntropy,
)

err := issuance.GenerateEntropy(input.Hash, input.Index)
if input.HasAnyIssuance() {
issuance, err := transaction.NewTxIssuanceFromInput(input)
if err != nil {
return err
}

asset, err := issuance.GenerateAsset()
if err != nil {
return err
Expand All @@ -352,10 +351,12 @@ func (b *blinder) blindInputs(unblinded []confidential.UnblindOutputResult) erro
return err
}

// if the token amount is not defined, it is set to 0x00, thus we need
// to check if the input.Issuance.TokenAmount, that is encoded in the
// elements format, contains more than one byte
if len(input.Issuance.TokenAmount) > 1 {
// ONLY in case the issuance is not a reissuance, if the token amount is
// not defined, it is set to 0x00, thus it's required to check that the
// input.Issuance.TokenAmount, that's encoded in the elements format (!),
// is longer than one byte. Reissuances, instead, cannot have a token
// amount defined.
if i := input.Issuance; !i.IsReissuance() && i.HasTokenAmount() {
token, err := issuance.GenerateReissuanceToken(
ConfidentialReissuanceTokenFlag,
)
Expand Down Expand Up @@ -686,7 +687,7 @@ func verifyBlinding(
inAssetBlinders = append(inAssetBlinders, unblinded.AssetBlindingFactor)
}

if txIn := pset.UnsignedTx.Inputs[i]; txIn.HasIssuance() {
if txIn := pset.UnsignedTx.Inputs[i]; txIn.HasAnyIssuance() {
if txIn.HasConfidentialIssuance() {
unblinded, err := confidential.UnblindIssuance(txIn, inIssuanceKeys[i].ToSlice())
if err != nil {
Expand All @@ -698,7 +699,7 @@ func verifyBlinding(
unblinded.Asset.AssetBlindingFactor,
)

if len(txIn.Issuance.TokenAmount) > 0 {
if txIn.Issuance.HasTokenAmount() {
inIssuanceAssets = append(inIssuanceAssets, unblinded.Token.Asset)
inIssuanceAssetBlinders = append(
inIssuanceAssetBlinders,
Expand All @@ -720,7 +721,7 @@ func verifyBlinding(
transaction.Zero[:],
)

if len(txIn.Issuance.TokenAmount) > 0 {
if txIn.Issuance.HasTokenAmount() {
token, err := iss.GenerateReissuanceToken(0)
if err != nil {
return false
Expand Down
Loading