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

feat: support short acp address #10

Merged
merged 5 commits into from
Nov 25, 2020
Merged
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
52 changes: 40 additions & 12 deletions address/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package address
import (
"encoding/hex"
"errors"
"github.com/nervosnetwork/ckb-sdk-go/utils"
"strings"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -22,32 +23,42 @@ const (
TypeFull Type = "Full"
TypeShort Type = "Short"

SHORT_FORMAT = "01"
FULL_DATA_FORMAT = "02"
FULL_TYPE_FORMAT = "04"
CODE_HASH_INDEX_SINGLESIG = "00"
CODE_HASH_INDEX_MULTISIG_SIG = "01"
ShortFormat = "01"
FullDataFormat = "02"
FullTypeFormat = "04"
CodeHashIndexSingleSig = "00"
CodeHashIndexMultisigSig = "01"
CodeHashIndexAnyoneCanPay = "02"
)

var shortPayloadSupportedArgsLens = [2]int{20, 22}

type ParsedAddress struct {
Mode Mode
Type Type
Script *types.Script
}

func Generate(mode Mode, script *types.Script) (string, error) {
if script.HashType == types.HashTypeType && len(script.Args) == 20 {
if script.HashType == types.HashTypeType && isShortPayloadSupportedArgsLen(len(script.Args)) {
if transaction.SECP256K1_BLAKE160_SIGHASH_ALL_TYPE_HASH == script.CodeHash.String() {
// generate_short_payload_singlesig_address
payload := SHORT_FORMAT + CODE_HASH_INDEX_SINGLESIG + hex.EncodeToString(script.Args)
// generate_short_payload_singleSig_address
payload := ShortFormat + CodeHashIndexSingleSig + hex.EncodeToString(script.Args)
data, err := bech32.ConvertBits(common.FromHex(payload), 8, 5, true)
if err != nil {
return "", err
}
return bech32.Encode((string)(mode), data)
} else if transaction.SECP256K1_BLAKE160_MULTISIG_ALL_TYPE_HASH == script.CodeHash.String() {
// generate_short_payload_multisig_address
payload := SHORT_FORMAT + CODE_HASH_INDEX_MULTISIG_SIG + hex.EncodeToString(script.Args)
payload := ShortFormat + CodeHashIndexMultisigSig + hex.EncodeToString(script.Args)
data, err := bech32.ConvertBits(common.FromHex(payload), 8, 5, true)
if err != nil {
return "", err
}
return bech32.Encode((string)(mode), data)
} else if utils.AnyoneCanPayCodeHashOnLina == script.CodeHash.String() || utils.AnyoneCanPayCodeHashOnAggron == script.CodeHash.String() {
payload := ShortFormat + CodeHashIndexAnyoneCanPay + hex.EncodeToString(script.Args)
data, err := bech32.ConvertBits(common.FromHex(payload), 8, 5, true)
if err != nil {
return "", err
Expand All @@ -56,14 +67,21 @@ func Generate(mode Mode, script *types.Script) (string, error) {
}
}

hashType := FULL_TYPE_FORMAT
hashType := FullTypeFormat
if script.HashType == types.HashTypeData {
hashType = FULL_DATA_FORMAT
hashType = FullDataFormat
}

return generateFullPayloadAddress(hashType, mode, script)
}

func isShortPayloadSupportedArgsLen(argLen int) bool {
if argLen >= shortPayloadSupportedArgsLens[0] && argLen <= shortPayloadSupportedArgsLens[1] {
return true
}
return false
}

func generateFullPayloadAddress(hashType string, mode Mode, script *types.Script) (string, error) {
payload := hashType + hex.EncodeToString(script.CodeHash.Bytes()) + hex.EncodeToString(script.Args)
data, err := bech32.ConvertBits(common.FromHex(payload), 8, 5, true)
Expand All @@ -88,12 +106,22 @@ func Parse(address string) (*ParsedAddress, error) {
var script types.Script
if strings.HasPrefix(payload, "01") {
addressType = TypeShort
if CODE_HASH_INDEX_SINGLESIG == payload[2:4] {
if CodeHashIndexSingleSig == payload[2:4] {
script = types.Script{
CodeHash: types.HexToHash(transaction.SECP256K1_BLAKE160_SIGHASH_ALL_TYPE_HASH),
HashType: types.HashTypeType,
Args: common.Hex2Bytes(payload[4:]),
}
} else if CodeHashIndexAnyoneCanPay == payload[2:4] {
script = types.Script{
HashType: types.HashTypeType,
Args: common.Hex2Bytes(payload[4:]),
}
if hrp == (string)(Testnet) {
script.CodeHash = types.HexToHash(utils.AnyoneCanPayCodeHashOnAggron)
} else {
script.CodeHash = types.HexToHash(utils.AnyoneCanPayCodeHashOnLina)
}
} else {
script = types.Script{
CodeHash: types.HexToHash(transaction.SECP256K1_BLAKE160_MULTISIG_ALL_TYPE_HASH),
Expand Down
206 changes: 206 additions & 0 deletions address/address_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package address

import (
"github.com/nervosnetwork/ckb-sdk-go/utils"
"testing"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -24,6 +25,91 @@ func TestGenerate(t *testing.T) {
tnAddress, err := Generate(Testnet, script)
assert.Nil(t, err)
assert.Equal(t, "ckt1qyqwmndf2yl6qvxwgvyw9yj95gkqytgygwasdjf6hm", tnAddress)

t.Run("generate short payload acp address without minimum limit", func(t *testing.T) {
mAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnLina),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a"),
}

mAddress, err := Generate(Mainnet, mAcpLock)
assert.Nil(t, err)
assert.Equal(t, "ckb1qypylv479ewscx3ms620sv34pgeuz6zagaaqvrugu7", mAddress)

tAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnAggron),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a"),
}
tAddress, err := Generate(Testnet, tAcpLock)
assert.Nil(t, err)
assert.Equal(t, "ckt1qypylv479ewscx3ms620sv34pgeuz6zagaaq3xzhsz", tAddress)
})

t.Run("generate short payload acp address with ckb minimum limit", func(t *testing.T) {
mAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnLina),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c"),
}

mAddress, err := Generate(Mainnet, mAcpLock)
assert.Nil(t, err)
assert.Equal(t, "ckb1qypylv479ewscx3ms620sv34pgeuz6zagaaqcehzz9g", mAddress)

tAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnAggron),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c"),
}
tAddress, err := Generate(Testnet, tAcpLock)
assert.Nil(t, err)
assert.Equal(t, "ckt1qypylv479ewscx3ms620sv34pgeuz6zagaaqc9q8fqw", tAddress)
})

t.Run("generate short payload acp address with ckb and udt minimum limit", func(t *testing.T) {
mAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnLina),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c01"),
}

mAddress, err := Generate(Mainnet, mAcpLock)
assert.Nil(t, err)
assert.Equal(t, "ckb1qypylv479ewscx3ms620sv34pgeuz6zagaaqcqgzc5xlw", mAddress)

tAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnAggron),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c01"),
}
tAddress, err := Generate(Testnet, tAcpLock)
assert.Nil(t, err)
assert.Equal(t, "ckt1qypylv479ewscx3ms620sv34pgeuz6zagaaqcqgr072sz", tAddress)
})

t.Run("generate full payload address when acp lock args is more than 22 bytes", func(t *testing.T) {
mAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnLina),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c0101"),
}

mAddress, err := Generate(Mainnet, mAcpLock)
assert.Nil(t, err)
assert.Equal(t, "ckb1qnfkjktl73ljn77q637judm4xux3y59c29qvvu8ywx90wy5c8g34gnajhch96rq68wrff7pjx59r8stgt4rh5rqpqy532xj3", mAddress)

tAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnAggron),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c0101"),
}

tAddress, err := Generate(Testnet, tAcpLock)
assert.Nil(t, err)
assert.Equal(t, "ckt1qs6pngwqn6e9vlm92th84rk0l4jp2h8lurchjmnwv8kq3rt5psf4vnajhch96rq68wrff7pjx59r8stgt4rh5rqpqy2a9ak4", tAddress)
})
}

func TestParse(t *testing.T) {
Expand All @@ -41,4 +127,124 @@ func TestParse(t *testing.T) {
assert.Equal(t, script.CodeHash, mnAddress.Script.CodeHash)
assert.Equal(t, script.HashType, mnAddress.Script.HashType)
assert.Equal(t, script.Args, mnAddress.Script.Args)

t.Run("parse short payload acp address without minimum limit", func(t *testing.T) {
mAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnLina),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a"),
}

mParsedAddress, err := Parse("ckb1qypylv479ewscx3ms620sv34pgeuz6zagaaqvrugu7")
assert.Nil(t, err)
assert.Equal(t, Mainnet, mParsedAddress.Mode)
assert.Equal(t, TypeShort, mParsedAddress.Type)
assert.Equal(t, mAcpLock.CodeHash, mParsedAddress.Script.CodeHash)
assert.Equal(t, mAcpLock.HashType, mParsedAddress.Script.HashType)
assert.Equal(t, mAcpLock.Args, mParsedAddress.Script.Args)

tAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnAggron),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a"),
}

tParsedAddress, err := Parse("ckt1qypylv479ewscx3ms620sv34pgeuz6zagaaq3xzhsz")
assert.Nil(t, err)
assert.Equal(t, Testnet, tParsedAddress.Mode)
assert.Equal(t, TypeShort, tParsedAddress.Type)
assert.Equal(t, tAcpLock.CodeHash, tParsedAddress.Script.CodeHash)
assert.Equal(t, tAcpLock.HashType, tParsedAddress.Script.HashType)
assert.Equal(t, tAcpLock.Args, tParsedAddress.Script.Args)
})

t.Run("parse short payload acp address with ckb minimum limit", func(t *testing.T) {
mAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnLina),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c"),
}

mParsedAddress, err := Parse("ckb1qypylv479ewscx3ms620sv34pgeuz6zagaaqcehzz9g")
assert.Nil(t, err)
assert.Equal(t, Mainnet, mParsedAddress.Mode)
assert.Equal(t, TypeShort, mParsedAddress.Type)
assert.Equal(t, mAcpLock.CodeHash, mParsedAddress.Script.CodeHash)
assert.Equal(t, mAcpLock.HashType, mParsedAddress.Script.HashType)
assert.Equal(t, mAcpLock.Args, mParsedAddress.Script.Args)

tAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnAggron),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c"),
}

tParsedAddress, err := Parse("ckt1qypylv479ewscx3ms620sv34pgeuz6zagaaqc9q8fqw")
assert.Nil(t, err)
assert.Equal(t, Testnet, tParsedAddress.Mode)
assert.Equal(t, TypeShort, tParsedAddress.Type)
assert.Equal(t, tAcpLock.CodeHash, tParsedAddress.Script.CodeHash)
assert.Equal(t, tAcpLock.HashType, tParsedAddress.Script.HashType)
assert.Equal(t, tAcpLock.Args, tParsedAddress.Script.Args)
})

t.Run("parse short payload acp address with ckb minimum limit and udt minimum limit", func(t *testing.T) {
mAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnLina),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c01"),
}

mParsedAddress, err := Parse("ckb1qypylv479ewscx3ms620sv34pgeuz6zagaaqcqgzc5xlw")
assert.Nil(t, err)
assert.Equal(t, Mainnet, mParsedAddress.Mode)
assert.Equal(t, TypeShort, mParsedAddress.Type)
assert.Equal(t, mAcpLock.CodeHash, mParsedAddress.Script.CodeHash)
assert.Equal(t, mAcpLock.HashType, mParsedAddress.Script.HashType)
assert.Equal(t, mAcpLock.Args, mParsedAddress.Script.Args)

tAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnAggron),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c01"),
}

tParsedAddress, err := Parse("ckt1qypylv479ewscx3ms620sv34pgeuz6zagaaqcqgr072sz")
assert.Nil(t, err)
assert.Equal(t, Testnet, tParsedAddress.Mode)
assert.Equal(t, TypeShort, tParsedAddress.Type)
assert.Equal(t, tAcpLock.CodeHash, tParsedAddress.Script.CodeHash)
assert.Equal(t, tAcpLock.HashType, tParsedAddress.Script.HashType)
assert.Equal(t, tAcpLock.Args, tParsedAddress.Script.Args)
})

t.Run("parse full payload acp address with args more than 22 bytes", func(t *testing.T) {
mAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnLina),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c0101"),
}

mParsedAddress, err := Parse("ckb1qnfkjktl73ljn77q637judm4xux3y59c29qvvu8ywx90wy5c8g34gnajhch96rq68wrff7pjx59r8stgt4rh5rqpqy532xj3")
assert.Nil(t, err)
assert.Equal(t, Mainnet, mParsedAddress.Mode)
assert.Equal(t, TypeFull, mParsedAddress.Type)
assert.Equal(t, mAcpLock.CodeHash, mParsedAddress.Script.CodeHash)
assert.Equal(t, mAcpLock.HashType, mParsedAddress.Script.HashType)
assert.Equal(t, mAcpLock.Args, mParsedAddress.Script.Args)

tAcpLock := &types.Script{
CodeHash: types.HexToHash(utils.AnyoneCanPayCodeHashOnAggron),
HashType: types.HashTypeType,
Args: common.FromHex("0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c0101"),
}

tParsedAddress, err := Parse("ckt1qs6pngwqn6e9vlm92th84rk0l4jp2h8lurchjmnwv8kq3rt5psf4vnajhch96rq68wrff7pjx59r8stgt4rh5rqpqy2a9ak4")
assert.Nil(t, err)
assert.Equal(t, Testnet, tParsedAddress.Mode)
assert.Equal(t, TypeFull, tParsedAddress.Type)
assert.Equal(t, tAcpLock.CodeHash, tParsedAddress.Script.CodeHash)
assert.Equal(t, tAcpLock.HashType, tParsedAddress.Script.HashType)
assert.Equal(t, tAcpLock.Args, tParsedAddress.Script.Args)
})
}
5 changes: 5 additions & 0 deletions utils/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import (
"github.com/nervosnetwork/ckb-sdk-go/types"
)

const (
AnyoneCanPayCodeHashOnLina = "0xd369597ff47f29fbc0d47d2e3775370d1250b85140c670e4718af712983a2354"
AnyoneCanPayCodeHashOnAggron = "0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356"
)

type SystemScriptCell struct {
CellHash types.Hash
OutPoint *types.OutPoint
Expand Down