diff --git a/address/address.go b/address/address.go index 0a278504..df7bc5cb 100644 --- a/address/address.go +++ b/address/address.go @@ -1,288 +1,229 @@ package address import ( - "encoding/hex" - "strings" - - "github.com/nervosnetwork/ckb-sdk-go/utils" - "github.com/pkg/errors" - - "github.com/ethereum/go-ethereum/common" - + "errors" + "fmt" "github.com/nervosnetwork/ckb-sdk-go/crypto/bech32" - "github.com/nervosnetwork/ckb-sdk-go/transaction" "github.com/nervosnetwork/ckb-sdk-go/types" ) -type Mode string -type Type string - -const ( - Mainnet Mode = "ckb" - Testnet Mode = "ckt" - - Short Type = "Short" - FullBech32 Type = "FullBech32" - FullBech32m Type = "FullBech32m" - - TYPE_FULL_WITH_BECH32M = "00" - ShortFormat = "01" - CodeHashIndexSingleSig = "00" - CodeHashIndexMultisigSig = "01" - CodeHashIndexAnyoneCanPay = "02" -) - -var shortPayloadSupportedArgsLens = [2]int{20, 22} - -type ParsedAddress struct { - Mode Mode - Type Type - Script *types.Script -} - -func ConvertScriptToAddress(mode Mode, script *types.Script) (string, error) { - return ConvertScriptToBech32mFullAddress(mode, script) +type Address struct { + Script *types.Script + Network types.Network } -// Deprecated: Short address format deprecated because it is limited (only support secp256k1_blake160, -// secp256k1_multisig, anyone_can_pay) and a flaw has been found in its encoding method bech32, -// which could enable attackers to generate valid but unexpected addresses. -// For more please check https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md -func ConvertScriptToShortAddress(mode Mode, script *types.Script) (string, error) { - 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 := 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 := 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 - } - return bech32.Encode((string)(mode), data) +func Decode(s string) (*Address, error) { + encoding, hrp, decoded, err := bech32.Decode(s) + if err != nil { + return nil, err + } + network, err := fromHrp(hrp) + if err != nil { + return nil, err + } + data, err := bech32.ConvertBits(decoded, 5, 8, false) + if err != nil { + return nil, err + } + switch data[0] { + case 0x00: + if encoding != bech32.BECH32M { + return nil, errors.New("payload header 0x00 must have encoding bech32m") } + return decodeLongBech32M(data, network) + case 0x01: + if encoding != bech32.BECH32 { + return nil, errors.New("payload header 0x01 must have encoding bech32") + } + return decodeShort(data, network) + case 0x02, 0x04: + if encoding != bech32.BECH32 { + return nil, errors.New("payload header 0x02 or 0x04 must have encoding bech32") + } + return decodeLongBech32(data, network) + default: + return nil, errors.New("unknown address format type") } - return "", errors.New("The given script can not be converted into short address. Unsupported") } -func isShortPayloadSupportedArgsLen(argLen int) bool { - if argLen >= shortPayloadSupportedArgsLens[0] && argLen <= shortPayloadSupportedArgsLens[1] { - return true +func decodeShort(payload []byte, network types.Network) (*Address, error) { + codeHashIndex := payload[1] + args := payload[2:] + argsLen := len(args) + var scriptType types.BuiltinScript + switch codeHashIndex { + case 0x00: // secp256k1_blake160_sighash_all + if argsLen != 20 { + return nil, errors.New(fmt.Sprintf("invalid args length %d", argsLen)) + } + scriptType = types.BuiltinScriptSecp256k1Blake160SighashAll + case 0x01: // secp256k1_blake160_multisig_all + if argsLen != 20 { + return nil, errors.New(fmt.Sprintf("invalid args length %d", argsLen)) + } + scriptType = types.BuiltinScriptSecp256k1Blake160MultisigAll + case 0x02: // anyone_can_pay + if argsLen < 20 || argsLen > 22 { + return nil, errors.New(fmt.Sprintf("invalid args length %d", argsLen)) + } + scriptType = types.BuiltinScriptAnyoneCanPay + default: + return nil, errors.New("unknown code hash index") } - return false + codeHash := types.GetCodeHash(scriptType, network) + return &Address{ + Script: &types.Script{ + CodeHash: codeHash, + HashType: types.HashTypeType, + Args: args, + }, + Network: network, + }, nil } -// Deprecated: Old full address format is deprecated because a flaw has been found in its encoding method -// bech32, which could enable attackers to generate valid but unexpected addresses. -// For more please check https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md -func ConvertScriptToFullAddress(mode Mode, script *types.Script) (string, error) { - var formatType string - switch script.HashType { - case "type": - formatType = "04" - case "data", "data1": - formatType = "02" +func decodeLongBech32(payload []byte, network types.Network) (*Address, error) { + var hashType types.ScriptHashType + switch payload[0] { + case 0x04: + hashType = types.HashTypeType + case 0x02: + hashType = types.HashTypeData default: - return "", errors.New("Unsupported hash type") - } + return nil, errors.New("unknown script hash type") + } + codeHash := types.BytesToHash(payload[1:33]) + args := payload[33:] + return &Address{ + Script: &types.Script{ + CodeHash: codeHash, + HashType: hashType, + Args: args, + }, + Network: network, + }, nil +} - payload := formatType + hex.EncodeToString(script.CodeHash.Bytes()) + hex.EncodeToString(script.Args) - data, err := bech32.ConvertBits(common.FromHex(payload), 8, 5, true) +func decodeLongBech32M(payload []byte, network types.Network) (*Address, error) { + if payload[0] != 0x00 { + return nil, errors.New(fmt.Sprintf("invalid payload header 0x%d", payload[0])) + } + codeHash := types.BytesToHash(payload[1:33]) + hashType, err := types.DeserializeHashTypeByte(payload[33]) if err != nil { - return "", err + return nil, err } - return bech32.Encode((string)(mode), data) + args := payload[34:] + return &Address{ + Script: &types.Script{ + CodeHash: codeHash, + HashType: hashType, + Args: args, + }, + Network: network, + }, nil +} + +func (a Address) Encode() (string, error) { + return a.EncodeFullBech32m() } -func ConvertScriptToBech32mFullAddress(mode Mode, script *types.Script) (string, error) { - hashType, err := types.SerializeHashType(script.HashType) +// EncodeShort encodes address in short format. +// +// See https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md for more details. +// +// Deprecated: Use EncodeFullBech32m instead. +func (a Address) EncodeShort() (string, error) { + payload := make([]byte, 0) + payload = append(payload, 0x01) + if a.Script.CodeHash == types.GetCodeHash(types.BuiltinScriptSecp256k1Blake160SighashAll, a.Network) { + payload = append(payload, 0x00) + } else if a.Script.CodeHash == types.GetCodeHash(types.BuiltinScriptSecp256k1Blake160MultisigAll, a.Network) { + payload = append(payload, 0x01) + } else if a.Script.CodeHash == types.GetCodeHash(types.BuiltinScriptAnyoneCanPay, a.Network) { + payload = append(payload, 0x02) + } else { + return "", errors.New("encoding to short address for given script is unsupported") + } + payload = append(payload, a.Script.Args...) + payload, err := bech32.ConvertBits(payload, 8, 5, true) if err != nil { return "", err } - - // Payload: type(00) | code hash | hash type | args - payload := TYPE_FULL_WITH_BECH32M - payload += script.CodeHash.Hex()[2:] - payload += hashType - - payload += common.Bytes2Hex(script.Args) - - dataPart, err := bech32.ConvertBits(common.FromHex(payload), 8, 5, true) + hrp, err := toHrp(a.Network) if err != nil { return "", err } - return bech32.EncodeWithBech32m(string(mode), dataPart) + return bech32.Encode(hrp, payload) } -func ConvertToBech32mFullAddress(address string) (string, error) { - parsedAddress, err := Parse(address) +// EncodeFullBech32 encodes address in full format with bech32 encoding. +// +// See https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md for more details. +// +// Deprecated: Use EncodeFullBech32m instead. +func (a Address) EncodeFullBech32() (string, error) { + payload := make([]byte, 0) + if a.Script.HashType == types.HashTypeType { + payload = append(payload, 0x04) + } else if a.Script.HashType == types.HashTypeData { + payload = append(payload, 0x02) + } else { + return "", errors.New(string("unknown hash type " + a.Script.HashType)) + } + payload = append(payload, a.Script.CodeHash.Bytes()...) + payload = append(payload, a.Script.Args...) + payload, err := bech32.ConvertBits(payload, 8, 5, true) if err != nil { return "", err } - return ConvertScriptToBech32mFullAddress(parsedAddress.Mode, parsedAddress.Script) -} - -// Deprecated: Short address format deprecated because it is limited (only support secp256k1_blake160, -// secp256k1_multisig, anyone_can_pay) and a flaw has been found in its encoding method bech32, -// which could enable attackers to generate valid but unexpected addresses. -// For more please check https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md -func ConvertToShortAddress(address string) (string, error) { - parsedAddress, err := Parse(address) + hrp, err := toHrp(a.Network) if err != nil { return "", err } - return ConvertScriptToShortAddress(parsedAddress.Mode, parsedAddress.Script) + return bech32.Encode(hrp, payload) } -// Deprecated: Old full address format is deprecated because a flaw has been found in its encoding method -// bech32, which could enable attackers to generate valid but unexpected addresses. -// For more please check https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md -func ConvertToBech32FullAddress(address string) (string, error) { - parsedAddress, err := Parse(address) +// EncodeFullBech32m encodes address in full format with bech32m encoding. +// +// See https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md for more details. +func (a Address) EncodeFullBech32m() (string, error) { + payload := make([]byte, 0) + payload = append(payload, 0x00) + payload = append(payload, a.Script.CodeHash.Bytes()...) + hashType, err := types.SerializeHashTypeByte(a.Script.HashType) if err != nil { return "", err } - return ConvertScriptToFullAddress(parsedAddress.Mode, parsedAddress.Script) -} - -func ConvertPublicToAddress(mode Mode, publicKey string) (string, error) { - script := &types.Script{ - CodeHash: types.HexToHash(transaction.SECP256K1_BLAKE160_SIGHASH_ALL_TYPE_HASH), - HashType: types.HashTypeType, - Args: common.FromHex(publicKey), - } - return ConvertScriptToBech32mFullAddress(mode, script) -} - -func Parse(address string) (*ParsedAddress, error) { - encoding, hrp, decoded, err := bech32.Decode(address) - if err != nil { - return nil, err + payload = append(payload, hashType) + payload = append(payload, a.Script.Args...) + if payload, err = bech32.ConvertBits(payload, 8, 5, true); err != nil { + return "", err } - data, err := bech32.ConvertBits(decoded, 5, 8, false) + hrp, err := toHrp(a.Network) if err != nil { - return nil, err - } - payload := hex.EncodeToString(data) - - var addressType Type - var script types.Script - if strings.HasPrefix(payload, "01") { - if encoding != bech32.BECH32 { - return nil, errors.New("payload header 0x01 should have encoding BECH32") - } - addressType = Short - if CodeHashIndexSingleSig == payload[2:4] { - if len(payload) != 44 { - return nil, errors.New("payload bytes length of secp256k1-sighash-all " + - "short address should be 22") - } - 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] { - if len(payload) < 44 || len(payload) > 48 { - return nil, errors.New("payload bytes length of acp short address should between 22-24") - } - 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 if CodeHashIndexMultisigSig == payload[2:4] { - if len(payload) != 44 { - return nil, errors.New("payload bytes length of secp256k1-multisig-all " + - "short address should be 22") - } - script = types.Script{ - CodeHash: types.HexToHash(transaction.SECP256K1_BLAKE160_MULTISIG_ALL_TYPE_HASH), - HashType: types.HashTypeType, - Args: common.Hex2Bytes(payload[4:]), - } - } else { - return nil, errors.New("unknown code hash index " + payload[2:4]) - } - } else if strings.HasPrefix(payload, "02") { - if encoding != bech32.BECH32 { - return nil, errors.New("payload header 0x02 should have encoding BECH32"); - } - addressType = FullBech32 - script = types.Script{ - CodeHash: types.HexToHash(payload[2:66]), - HashType: types.HashTypeData, - Args: common.Hex2Bytes(payload[66:]), - } - } else if strings.HasPrefix(payload, "04") { - if encoding != bech32.BECH32 { - return nil, errors.New("payload header 0x04 should have encoding BECH32"); - } - addressType = FullBech32 - script = types.Script{ - CodeHash: types.HexToHash(payload[2:66]), - HashType: types.HashTypeType, - Args: common.Hex2Bytes(payload[66:]), - } - } else if strings.HasPrefix(payload, "00") { - if encoding != bech32.BECH32M { - return nil, errors.New("payload header 0x00 should have encoding BECH32"); - } - addressType = FullBech32m - script = types.Script{ - CodeHash: types.HexToHash(payload[2:66]), - Args: common.Hex2Bytes(payload[68:]), - } - - hashType, err := types.DeserializeHashType(payload[66:68]) - if err != nil { - return nil, err - } - - script.HashType = hashType - - } else { - return nil, errors.New("address type error:" + payload[:2]) - } - - result := &ParsedAddress{ - Mode: Mode(hrp), - Type: addressType, - Script: &script, + return "", err } - return result, nil + return bech32.EncodeWithBech32m(hrp, payload) } -func ValidateChequeAddress(addr string, systemScripts *utils.SystemScripts) (*ParsedAddress, error) { - parsedSenderAddr, err := Parse(addr) - if err != nil { - return nil, err - } - if isSecp256k1Lock(parsedSenderAddr, systemScripts) { - return parsedSenderAddr, nil +func toHrp(network types.Network) (string, error) { + switch network { + case types.NetworkMain: + return "ckb", nil + case types.NetworkTest: + return "ckt", nil + default: + return "", errors.New("unknown network") } - return nil, errors.Errorf("address %s is not an SECP256K1 short format address", addr) } -func isSecp256k1Lock(parsedSenderAddr *ParsedAddress, systemScripts *utils.SystemScripts) bool { - return parsedSenderAddr.Script.CodeHash == systemScripts.SecpSingleSigCell.CellHash && - parsedSenderAddr.Script.HashType == systemScripts.SecpSingleSigCell.HashType && - len(parsedSenderAddr.Script.Args) == 20 -} +func fromHrp(hrp string) (types.Network, error) { + switch hrp { + case "ckb": + return types.NetworkMain, nil + case "ckt": + return types.NetworkTest, nil + default: + return 0, errors.New("unknown hrp") + } +} \ No newline at end of file diff --git a/address/address_test.go b/address/address_test.go index f152a087..6d4bf55d 100644 --- a/address/address_test.go +++ b/address/address_test.go @@ -1,392 +1,148 @@ package address import ( - "context" - "github.com/nervosnetwork/ckb-sdk-go/mocks" - "github.com/nervosnetwork/ckb-sdk-go/utils" - "github.com/pkg/errors" - "reflect" - "testing" - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/assert" - "github.com/nervosnetwork/ckb-sdk-go/types" + "github.com/stretchr/testify/assert" + "testing" ) -func TestGenerate(t *testing.T) { - script := &types.Script{ - CodeHash: types.HexToHash("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"), - HashType: types.HashTypeType, - Args: common.FromHex("0xedcda9513fa030ce4308e29245a22c022d0443bb"), - } - - mnAddress, err := ConvertScriptToShortAddress(Mainnet, script) - - assert.Nil(t, err) - assert.Equal(t, "ckb1qyqwmndf2yl6qvxwgvyw9yj95gkqytgygwasshh9m8", mnAddress) - - tnAddress, err := ConvertScriptToShortAddress(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 := ConvertScriptToShortAddress(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 := ConvertScriptToShortAddress(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 := ConvertScriptToShortAddress(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 := ConvertScriptToShortAddress(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 := ConvertScriptToShortAddress(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 := ConvertScriptToShortAddress(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 := ConvertScriptToFullAddress(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 := ConvertScriptToFullAddress(Testnet, tAcpLock) - assert.Nil(t, err) - assert.Equal(t, "ckt1qs6pngwqn6e9vlm92th84rk0l4jp2h8lurchjmnwv8kq3rt5psf4vnajhch96rq68wrff7pjx59r8stgt4rh5rqpqy2a9ak4", tAddress) - }) +// test fixture comes from: https://github.com/rev-chaos/ckb-address-demo +func TestShort(t *testing.T) { + // sighash + s := generateScript( + "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "b39bbc0b3673c7d36450bc14cfcdad2d559c6c64", + types.HashTypeType) + testShort(t, s, types.NetworkMain, "ckb1qyqt8xaupvm8837nv3gtc9x0ekkj64vud3jqfwyw5v") + testShort(t, s, types.NetworkTest, "ckt1qyqt8xaupvm8837nv3gtc9x0ekkj64vud3jq5t63cs") + + // multisig + s = generateScript( + "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8", + "4fb2be2e5d0c1a3b8694f832350a33c1685d477a", + types.HashTypeType) + testShort(t, s, types.NetworkMain, "ckb1qyq5lv479ewscx3ms620sv34pgeuz6zagaaqklhtgg") + testShort(t, s, types.NetworkTest, "ckt1qyq5lv479ewscx3ms620sv34pgeuz6zagaaqt6f5y5") + + // acp mainnet + s = generateScript( + "0xd369597ff47f29fbc0d47d2e3775370d1250b85140c670e4718af712983a2354", + "bd07d9f32bce34d27152a6a0391d324f79aab854", + types.HashTypeType) + testShort(t, s, types.NetworkMain, "ckb1qypt6p7e7v4uudxjw9f2dgper5ey77d2hp2qxz4u4u") + // acp testnet + s = generateScript( + "0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356", + "bd07d9f32bce34d27152a6a0391d324f79aab854", + types.HashTypeType) + testShort(t, s, types.NetworkTest, "ckt1qypt6p7e7v4uudxjw9f2dgper5ey77d2hp2qm8treq") } -func TestParse(t *testing.T) { - script := &types.Script{ - CodeHash: types.HexToHash("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"), - HashType: types.HashTypeType, - Args: common.FromHex("0xedcda9513fa030ce4308e29245a22c022d0443bb"), +func testShort(t *testing.T, script *types.Script, network types.Network, encoded string) { + a := &Address{ + Script: script, + Network: network, } + result, err := a.EncodeShort() + if err != nil { + t.Error(err) + } + assert.Equal(t, encoded, result) + addr, err := Decode(encoded) + if err != nil { + t.Error(err) + } + assert.Equal(t, a, addr) +} - mnAddress, err := Parse("ckb1qyqwmndf2yl6qvxwgvyw9yj95gkqytgygwasshh9m8") - - assert.Nil(t, err) - assert.Equal(t, Mainnet, mnAddress.Mode) - assert.Equal(t, Short, mnAddress.Type) - 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, Short, 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, Short, 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, Short, 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, Short, 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) - - tParsedAddress, err = Parse("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4") - assert.Nil(t, err) - assert.Equal(t, Mainnet, tParsedAddress.Mode) - assert.Equal(t, FullBech32m, tParsedAddress.Type) - }) - - 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, Short, 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, Short, 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, FullBech32, 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, FullBech32, 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) - }) +func TestFullBech32(t *testing.T) { + s := generateScript( + "9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "b39bbc0b3673c7d36450bc14cfcdad2d559c6c64", + types.HashTypeData) + testFullBech32(t, s, types.NetworkMain, "ckb1q2da0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxgdwd2q8") + testFullBech32(t, s, types.NetworkTest, "ckt1q2da0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxgqd588c") + + s.HashType = types.HashTypeType + testFullBech32(t, s, types.NetworkMain, "ckb1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxgj53qks") + testFullBech32(t, s, types.NetworkTest, "ckt1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxglhgd30") } -func TestValidateChequeAddress(t *testing.T) { - type args struct { - addr string +func testFullBech32(t *testing.T, script *types.Script, network types.Network, encoded string) { + a := &Address{ + Script: script, + Network: network, } - tests := []struct { - name string - args args - want *ParsedAddress - wantErr error - chain string - }{ - { - "invalid address for testnet", - args{ - addr: "ckt1q3085d480e5wanqp8hazle4z8uakcdztqsq9szrfftnd630w5n8ath08sqwqw00mx3jv0v0stwqxhv4mhp8fjcjtac0", - }, - nil, - errors.Errorf("address %s is not an SECP256K1 short format address", "ckt1q3085d480e5wanqp8hazle4z8uakcdztqsq9szrfftnd630w5n8ath08sqwqw00mx3jv0v0stwqxhv4mhp8fjcjtac0"), - "ckt", - }, - { - "invalid address for miannet", - args{ - addr: "ckb1q3085d480e5wanqp8hazle4z8uakcdztqsq9szrfftnd630w5n8ath08sqwqw00mx3jv0v0stwqxhv4mhp8fj43jsls", - }, - nil, - errors.Errorf("address %s is not an SECP256K1 short format address", "ckb1q3085d480e5wanqp8hazle4z8uakcdztqsq9szrfftnd630w5n8ath08sqwqw00mx3jv0v0stwqxhv4mhp8fj43jsls"), - "ckb", - }, - { - "valid address for testnet", - args{ - addr: "ckt1qyqdmeuqrsrnm7e5vnrmruzmsp4m9wacf6vsmcwugu", - }, - &ParsedAddress{ - Mode: Testnet, - Type: Short, - Script: &types.Script{ - CodeHash: types.HexToHash("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"), - HashType: types.HashTypeType, - Args: common.FromHex("0xdde7801c073dfb3464c7b1f05b806bb2bbb84e99"), - }, - }, - nil, - "ckt", - }, - { - "valid address for miannet", - args{ - addr: "ckb1qyqdmeuqrsrnm7e5vnrmruzmsp4m9wacf6vsxasryq", - }, - &ParsedAddress{ - Mode: Mainnet, - Type: Short, - Script: &types.Script{ - CodeHash: types.HexToHash("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"), - HashType: types.HashTypeType, - Args: common.FromHex("0xdde7801c073dfb3464c7b1f05b806bb2bbb84e99"), - }, - }, - nil, - "ckb", - }, + result, err := a.EncodeFullBech32() + if err != nil { + t.Error(err) } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockClient := &mocks.Client{} - mockClient.On("GetBlockchainInfo", context.Background()).Return(&types.BlockchainInfo{Chain: tt.chain}, nil) - systemScripts, _ := utils.NewSystemScripts(mockClient) - got, err := ValidateChequeAddress(tt.args.addr, systemScripts) - if (err != nil) && err.Error() != tt.wantErr.Error() { - t.Errorf("ValidateAddress() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("ValidateAddress() got = %v, want %v", got, tt.want) - } - }) + assert.Equal(t, encoded, result) + addr, err := Decode(encoded) + if err != nil { + t.Error(err) } + assert.Equal(t, a, addr) } -func TestConvertShortAddressToBech32mFullAddress(t *testing.T) { - shortAddress := "ckt1qyqxgp7za7dajm5wzjkye52asc8fxvvqy9eqlhp82g" - bech32mFullAddress, err := ConvertToBech32mFullAddress(shortAddress) - if err != nil { - t.Errorf("Fail to convert deprecated address to bech32m full address. error = %v", err) - return - } - assert.Equal(t, "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqtyqlpwlx7ed68pftzv69wcvr5nxxqzzus2zxwa6", bech32mFullAddress) +func TestFullBech32m(t *testing.T) { + s := generateScript( + "9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "b39bbc0b3673c7d36450bc14cfcdad2d559c6c64", + types.HashTypeData) + testFullBech32m(t, s, types.NetworkMain, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq9nnw7qkdnnclfkg59uzn8umtfd2kwxceqvguktl") + testFullBech32m(t, s, types.NetworkTest, "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq9nnw7qkdnnclfkg59uzn8umtfd2kwxceqz6hep8") + + s.HashType = types.HashTypeType + testFullBech32m(t, s, types.NetworkMain, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4") + testFullBech32m(t, s, types.NetworkTest, "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqgutnjd") + + s.HashType = types.HashTypeData1 + testFullBech32m(t, s, types.NetworkMain, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq4nnw7qkdnnclfkg59uzn8umtfd2kwxceqcydzyt") + testFullBech32m(t, s, types.NetworkTest, "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq4nnw7qkdnnclfkg59uzn8umtfd2kwxceqkkxdwn") } -func TestBech32mFullAddressToShortAddress(t *testing.T) { - bech32mFullAddress := "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqtyqlpwlx7ed68pftzv69wcvr5nxxqzzus2zxwa6" - shortAddress, err := ConvertToShortAddress(bech32mFullAddress) +func testFullBech32m(t *testing.T, script *types.Script, network types.Network, encoded string) { + a := &Address{ + Script: script, + Network: network, + } + result, err := a.EncodeFullBech32m() + if err != nil { + t.Error(err) + } + assert.Equal(t, encoded, result) + addr, err := Decode(encoded) if err != nil { - t.Errorf("Fail to convert deprecated address to bech32m full address. error = %v", err) - return + t.Error(err) } - assert.Equal(t, "ckt1qyqxgp7za7dajm5wzjkye52asc8fxvvqy9eqlhp82g", shortAddress) + assert.Equal(t, a, addr) } -func TestConvertPublickeyToBech32mFullAddress(t *testing.T) { - address, err := ConvertPublicToAddress(Mainnet, "0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64") - if err != nil { - t.Errorf("Fail to convert public key to bech32m full address. error = %v", err) - return +func generateScript(codeHash string, args string, hashType types.ScriptHashType) *types.Script { + return &types.Script{ + CodeHash: types.HexToHash(codeHash), + HashType: hashType, + Args: common.FromHex(args), } - assert.Equal(t, address, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4") } -func TestInvalidAddressParse(t *testing.T) { - // These invalid address come form https://github.com/nervosnetwork/ckb-sdk-rust/pull/7/files - // INVALID bech32 encoding - _, err := Parse("ckb1qyqylv479ewscx3ms620sv34pgeuz6zagaaqh0knz7") +// These invalid addresses come form https://github.com/nervosnetwork/ckb-sdk-rust/pull/7/files +func TestInvalidDecode(t *testing.T) { + var err error + _, err = Decode("ckb1qyqylv479ewscx3ms620sv34pgeuz6zagaaqh0knz7") assert.NotNil(t, err) - // INVALID data length - _, err = Parse("ckb1qyqylv479ewscx3ms620sv34pgeuz6zagaarxdzvx03") + _, err = Decode("ckb1qyqylv479ewscx3ms620sv34pgeuz6zagaarxdzvx03") assert.NotNil(t, err) - // INVALID code hash index - _, err = Parse("ckb1qyg5lv479ewscx3ms620sv34pgeuz6zagaaqajch0c") + _, err = Decode("ckb1qyg5lv479ewscx3ms620sv34pgeuz6zagaaqajch0c") assert.NotNil(t, err) - // INVALID bech32m encoding - _, err = Parse("ckb1q2da0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsnajhch96rq68wrqn2tmhm") + _, err = Decode("ckb1q2da0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsnajhch96rq68wrqn2tmhm") assert.NotNil(t, err) - // Invalid ckb2021 format full address - _, err = Parse("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq20k2lzuhgvrgacv4tmr88") + _, err = Decode("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq20k2lzuhgvrgacv4tmr88") assert.NotNil(t, err) - _, err = Parse("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqz0k2lzuhgvrgacvhcym08") + _, err = Decode("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqz0k2lzuhgvrgacvhcym08") assert.NotNil(t, err) - _, err = Parse("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqj0k2lzuhgvrgacvnhnzl8") + _, err = Decode("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqj0k2lzuhgvrgacvnhnzl8") assert.NotNil(t, err) -} +} \ No newline at end of file diff --git a/address/address_tools.go b/address/address_tools.go index 98f4f11e..4c0b8f84 100644 --- a/address/address_tools.go +++ b/address/address_tools.go @@ -1,209 +1,111 @@ package address import ( - "bytes" - "encoding/hex" + "errors" "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/nervosnetwork/ckb-sdk-go/crypto/blake2b" "github.com/nervosnetwork/ckb-sdk-go/crypto/secp256k1" - "github.com/nervosnetwork/ckb-sdk-go/transaction" + "github.com/nervosnetwork/ckb-sdk-go/transaction/signer" "github.com/nervosnetwork/ckb-sdk-go/types" - "github.com/pkg/errors" + "github.com/nervosnetwork/ckb-sdk-go/utils" ) -const ( - MAINNET_ACP_CODE_HASH = "0xd369597ff47f29fbc0d47d2e3775370d1250b85140c670e4718af712983a2354" - TESTNET_ACP_CODE_HASH = "0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356" - MAINNET_CHEQUE_CODE_HASH = "0xe4d4ecc6e5f9a059bf2f7a82cca292083aebc0c421566a52484fe2ec51a9fb0c" - TESTNET_CHEQUE_CODE_HASH = "0x60d5f39efce409c587cb9ea359cefdead650ca128f0bd9cb3855348f98c70d5b" -) - -type AddressGenerateResult struct { - Address string - LockArgs string - PrivateKey string -} - -func GenerateAddress(mode Mode) (*AddressGenerateResult, error) { - return GenerateBech32mFullAddress(mode) -} - -// Deprecated: Short address format deprecated because it is limited (only support secp256k1_blake160, -// secp256k1_multisig, anyone_can_pay) and a flaw has been found in its encoding method bech32, -// which could enable attackers to generate valid but unexpected addresses. -// For more please check https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md -func GenerateShortAddress(mode Mode) (*AddressGenerateResult, error) { - - key, err := secp256k1.RandomNew() - if err != nil { - return nil, err - } - - pubKey, err := blake2b.Blake160(key.PubKey()) - if err != nil { - return nil, err - } - - script := &types.Script{ - CodeHash: types.HexToHash(transaction.SECP256K1_BLAKE160_SIGHASH_ALL_TYPE_HASH), +func GenerateScriptSecp256K1Blake160SignhashAll(key *secp256k1.Secp256k1Key) *types.Script { + args, _ := blake2b.Blake160(key.PubKey()) + return &types.Script{ + // The same code hash is shared by mainnet and testnet + CodeHash: types.GetCodeHash(types.BuiltinScriptSecp256k1Blake160SighashAll, types.NetworkMain), HashType: types.HashTypeType, - Args: common.FromHex(hex.EncodeToString(pubKey)), + Args: args, } - - address, err := ConvertScriptToShortAddress(mode, script) - if err != nil { - return nil, err - } - - return &AddressGenerateResult{ - Address: address, - LockArgs: hexutil.Encode(pubKey), - PrivateKey: hexutil.Encode(key.Bytes()), - }, err } -// Deprecated: Old full address format is deprecated because a flaw has been found in its encoding method -// bech32, which could enable attackers to generate valid but unexpected addresses. -// For more please check https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md -func GenerateFullAddress(mode Mode) (*AddressGenerateResult, error) { - key, err := secp256k1.RandomNew() - if err != nil { - return nil, err - } - - pubKey, err := blake2b.Blake160(key.PubKey()) - if err != nil { - return nil, err +func GenerateScriptSecp256K1Blake160SignhashAllByPublicKey(pubKey string) (*types.Script, error) { + b := common.FromHex(pubKey) + if len(b) != 33 { + return nil, errors.New("only accept 33-byte compressed public key") } - - script := &types.Script{ - CodeHash: types.HexToHash(transaction.SECP256K1_BLAKE160_SIGHASH_ALL_TYPE_HASH), + args, _ := blake2b.Blake160(b) + return &types.Script{ + // The same code hash is shared by mainnet and testnet + CodeHash: types.GetCodeHash(types.BuiltinScriptSecp256k1Blake160SighashAll, types.NetworkMain), HashType: types.HashTypeType, - Args: common.FromHex(hex.EncodeToString(pubKey)), - } - - address, err := ConvertScriptToFullAddress(mode, script) - if err != nil { - return nil, err - } - - return &AddressGenerateResult{ - Address: address, - LockArgs: hexutil.Encode(pubKey), - PrivateKey: hexutil.Encode(key.Bytes()), - }, err + Args: args, + }, nil } -func GenerateBech32mFullAddress(mode Mode) (*AddressGenerateResult, error) { - key, err := secp256k1.RandomNew() - if err != nil { - return nil, err +func GenerateAddressSecp256K1Blake160SignhashAll(key *secp256k1.Secp256k1Key, network types.Network) *Address { + script := GenerateScriptSecp256K1Blake160SignhashAll(key) + return &Address{ + Script: script, + Network: network, } +} - pubKey, err := blake2b.Blake160(key.PubKey()) +func ValidateChequeAddress(addr string, systemScripts *utils.SystemScripts) (*Address, error) { + address, err := Decode(addr) if err != nil { return nil, err } - - script := &types.Script{ - CodeHash: types.HexToHash(transaction.SECP256K1_BLAKE160_SIGHASH_ALL_TYPE_HASH), - HashType: types.HashTypeType, - Args: common.FromHex(hex.EncodeToString(pubKey)), + if isSecp256k1Lock(address, systemScripts) { + return address, nil } - - address, err := ConvertScriptToBech32mFullAddress(mode, script) - if err != nil { - return nil, err - } - - return &AddressGenerateResult{ - Address: address, - LockArgs: hexutil.Encode(pubKey), - PrivateKey: hexutil.Encode(key.Bytes()), - }, err + return nil, errors.New(fmt.Sprintf("address %s is not an SECP256K1 short format address", addr)) } -func GenerateAcpAddress(secp256k1Address string) (string, error) { - addressScript, err := Parse(secp256k1Address) - if err != nil { - return "", err - } - - script := &types.Script{ - CodeHash: types.HexToHash(getAcpCodeHash(addressScript.Mode)), - HashType: types.HashTypeType, - Args: common.FromHex(hex.EncodeToString(addressScript.Script.Args)), - } - - return ConvertScriptToAddress(addressScript.Mode, script) +func isSecp256k1Lock(parsedSenderAddr *Address, systemScripts *utils.SystemScripts) bool { + return parsedSenderAddr.Script.CodeHash == systemScripts.SecpSingleSigCell.CellHash && + parsedSenderAddr.Script.HashType == systemScripts.SecpSingleSigCell.HashType && + len(parsedSenderAddr.Script.Args) == 20 } -func GenerateChequeAddress(senderAddress, receiverAddress string) (string, error) { - senderScript, err := Parse(senderAddress) - if err != nil { - return "", err - } - receiverScript, err := Parse(receiverAddress) - if err != nil { - return "", err - } - - if senderScript.Mode != receiverScript.Mode { - return "", errors.New("The network type of senderAddress and receiverAddress must be the same") - } - - senderScriptHash, err := senderScript.Script.Hash() - if err != nil { - return "", err - } - receiverScriptHash, err := receiverScript.Script.Hash() +// GenerateSecp256k1Blake160MultisigScript generate scep256k1 multisig script. +// It can accept public key (in compressed format, 33 bytes each) array or public key hash (20 bytes) array, and +// return error if giving none of them. +func GenerateSecp256k1Blake160MultisigScript(requireN, threshold int, publicKeysOrHashes [][]byte) (*types.Script, []byte, error) { + multisigScript := signer.MultisigScript{ + Version: 0, + FirstN: byte(requireN), + Threshold: byte(threshold), + KeysHashes: [][20]byte{}, + } + + isPublicKeyHash := len(publicKeysOrHashes[0]) == 20 + if isPublicKeyHash { + for _, publicKeyHash := range publicKeysOrHashes { + if len(publicKeyHash) != 20 { + return nil, nil, errors.New("public key hash length must be 20 bytes") + } + if err := multisigScript.AddKeyHashBySlice(publicKeyHash); err != nil { + return nil, nil, err + } + } + } else { + for _, publicKey := range publicKeysOrHashes { + if len(publicKey) != 33 { + return nil, nil, errors.New("public key (compressed) length must be 33 bytes") + } + publicKeyHash, err := blake2b.Blake160(publicKey) + if err != nil { + return nil, nil, err + } + if err := multisigScript.AddKeyHashBySlice(publicKeyHash); err != nil { + return nil, nil, err + } + } + } + + args, err := multisigScript.ComputeHash() if err != nil { - return "", err + return nil, nil, err } - s1 := senderScriptHash.String()[0:42] - s2 := receiverScriptHash.String()[0:42] - - args := bytesCombine(common.FromHex(s2), common.FromHex(s1)) - pubKey := common.Bytes2Hex(args) - fmt.Printf("pubKey: %s\n", pubKey) - - chequeLock := &types.Script{ - CodeHash: types.HexToHash(getChequeCodeHash(senderScript.Mode)), + // secp256k1_blake160_multisig_all share the same code hash in network main and test + codeHash := types.GetCodeHash(types.BuiltinScriptSecp256k1Blake160MultisigAll, types.NetworkTest) + return &types.Script{ + CodeHash: codeHash, HashType: types.HashTypeType, - Args: common.FromHex(pubKey), - } - - return ConvertScriptToAddress(senderScript.Mode, chequeLock) - -} - -func getHashType(hashType types.ScriptHashType) string { - if hashType == types.HashTypeType { - return "01" - } else { - return "00" - } -} - -func getAcpCodeHash(mode Mode) string { - if mode == Mainnet { - return MAINNET_ACP_CODE_HASH - } else { - return TESTNET_ACP_CODE_HASH - } -} - -func getChequeCodeHash(mode Mode) string { - if mode == Mainnet { - return MAINNET_CHEQUE_CODE_HASH - } else { - return TESTNET_CHEQUE_CODE_HASH - } -} - -func bytesCombine(pBytes ...[]byte) []byte { - return bytes.Join(pBytes, []byte("")) -} + Args: args, + }, multisigScript.Encode(), nil +} \ No newline at end of file diff --git a/address/address_tools_test.go b/address/address_tools_test.go index 478f600c..81ba7678 100644 --- a/address/address_tools_test.go +++ b/address/address_tools_test.go @@ -1,118 +1,114 @@ package address import ( - "fmt" + "encoding/hex" "github.com/ethereum/go-ethereum/common" + "github.com/nervosnetwork/ckb-sdk-go/crypto/blake2b" + "github.com/nervosnetwork/ckb-sdk-go/crypto/secp256k1" "github.com/nervosnetwork/ckb-sdk-go/types" "github.com/stretchr/testify/assert" "testing" ) -func TestGenerateShortAddress(t *testing.T) { - shortAddress, _ := GenerateShortAddress(Mainnet) - fmt.Println(shortAddress) -} - -func TestGenerateAcpAddress(t *testing.T) { - address := "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqg958atl2zdh8jn3ch8lc72nt0cf864ecqdxm9zf" - acpAddress, err := GenerateAcpAddress(address) - assert.Nil(t, err) - assert.Equal(t, "ckt1qq6pngwqn6e9vlm92th84rk0l4jp2h8lurchjmnwv8kq3rt5psf4vqg958atl2zdh8jn3ch8lc72nt0cf864ecqz4aphl", acpAddress) -} - -func TestGenerateChequeAddress(t *testing.T) { - senderAddress := "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqd0pdquvfuq077aemn447shf4d8u5f4a0glzz2g4" - receiverAddress := "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqg958atl2zdh8jn3ch8lc72nt0cf864ecqdxm9zf" - acpAddress, err := GenerateChequeAddress(senderAddress, receiverAddress) - assert.Nil(t, err) - assert.Equal(t, "ckt1qpsdtuu7lnjqn3v8ew02xkwwlh4dv5x2z28shkwt8p2nfruccux4kq2je6sm0zczgrepc8y547zvuu6zpshfvvjh7h2ln2w035d2lnh32ylk5ydmjq5ypwq24ftzt", acpAddress) -} - -func TestBech32mTypeFullMainnetAddressGenerate(t *testing.T) { - script := &types.Script{ +func TestGenerateScriptSecp256K1Blake160SignhashAll(t *testing.T) { + key, err := secp256k1.HexToKey("e79f3207ea4980b7fed79956d5934249ceac4751a4fae01a0f7c4a96884bc4e3") + if err != nil { + t.Error(err) + } + generated := GenerateScriptSecp256K1Blake160SignhashAll(key) + expected := &types.Script{ CodeHash: types.HexToHash("0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"), HashType: types.HashTypeType, - Args: common.FromHex("0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"), + Args: common.FromHex("0x36c329ed630d6ce750712a477543672adab57f4c"), } + assert.Equal(t, expected, generated) - address, err := ConvertScriptToBech32mFullAddress(Mainnet, script) - println(address) + generated, err = GenerateScriptSecp256K1Blake160SignhashAllByPublicKey("0x024a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01") if err != nil { - return + t.Error(err) } - - assert.Equal(t, - "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4", - address) - - t.Run("parse full address", func(t *testing.T) { - mnAddress, err := Parse(address) - - assert.Nil(t, err) - assert.Equal(t, Mainnet, mnAddress.Mode) - assert.Equal(t, FullBech32m, mnAddress.Type) - assert.Equal(t, script.CodeHash, mnAddress.Script.CodeHash) - assert.Equal(t, script.HashType, mnAddress.Script.HashType) - assert.Equal(t, script.Args, mnAddress.Script.Args) - }) - + assert.Equal(t, expected, generated) + // test public key hex without 0x + generated, err = GenerateScriptSecp256K1Blake160SignhashAllByPublicKey("024a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01") + if err != nil { + t.Error(err) + } + assert.Equal(t, expected, generated) + // test invalid length + _, err = GenerateScriptSecp256K1Blake160SignhashAllByPublicKey("0x024a501ef") + assert.NotNil(t, err) + // test uncompressed public key + _, err = GenerateScriptSecp256K1Blake160SignhashAllByPublicKey("0x044a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01d1868c7dabbf50e52ca7311e1263f917a8ced1d033e82dc2a68bed69397382f4") + assert.NotNil(t, err) } -func TestBech32mDataFullMainnetAddressGenerate(t *testing.T) { - script := &types.Script{ - CodeHash: types.HexToHash("0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176"), - HashType: types.HashTypeData, - Args: common.FromHex("0x36c329ed630d6ce750712a477543672adab57f4c"), +func TestGenerateSecp256k1MultisigScript(t *testing.T) { + var publicKeys [][]byte + + key, err := hex.DecodeString("032edb83018b57ddeb9bcc7287c5cc5da57e6e0289d31c9e98cb361e88678d6288") + if err != nil { + t.Error(t, err) } + publicKeys = append(publicKeys, key) - address, err := ConvertScriptToBech32mFullAddress(Mainnet, script) - println(address) + key, err = hex.DecodeString("033aeb3fdbfaac72e9e34c55884a401ee87115302c146dd9e314677d826375dc8f") if err != nil { - return + t.Error(t, err) } + publicKeys = append(publicKeys, key) - assert.Equal(t, - "ckb1qzn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvqpkcv576ccddnn4quf2ga65xee2m26h7nqdcg257", - address) + key, err = hex.DecodeString("029a685b8206550ea1b600e347f18fd6115bffe582089d3567bec7eba57d04df01") + if err != nil { + t.Error(t, err) + } + publicKeys = append(publicKeys, key) - t.Run("parse full address", func(t *testing.T) { - mnAddress, err := Parse(address) + script, _, err := GenerateSecp256k1Blake160MultisigScript(0, 2, publicKeys) + if err != nil { + t.Error(t, err) + } - assert.Nil(t, err) - assert.Equal(t, Mainnet, mnAddress.Mode) - assert.Equal(t, FullBech32m, mnAddress.Type) - assert.Equal(t, script.CodeHash, mnAddress.Script.CodeHash) - assert.Equal(t, script.HashType, mnAddress.Script.HashType) - assert.Equal(t, script.Args, mnAddress.Script.Args) - }) + address := &Address{ + Script: script, + Network: types.NetworkTest, + } + encoded, err := address.Encode() + if err != nil { + t.Error(err) + } + assert.Equal(t, "ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sq0sfnkgf0ph76pkzwld9ujzex4pkeuwnlsdc5tqu", encoded) } -func TestBech32mData1FullMainnetAddressGenerate(t *testing.T) { - script := &types.Script{ - CodeHash: types.HexToHash("0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176"), - HashType: types.HashTypeData1, - Args: common.FromHex("0x36c329ed630d6ce750712a477543672adab57f4c"), +func TestGenerateSecp256k1MultisigScriptByHash(t *testing.T) { + publicKeysHex := []string{ + "032edb83018b57ddeb9bcc7287c5cc5da57e6e0289d31c9e98cb361e88678d6288", + "033aeb3fdbfaac72e9e34c55884a401ee87115302c146dd9e314677d826375dc8f", + "029a685b8206550ea1b600e347f18fd6115bffe582089d3567bec7eba57d04df01", } - - address, err := ConvertScriptToBech32mFullAddress(Mainnet, script) - println(address) + var publicKeysHash [][]byte + for _, publicKeyHex := range publicKeysHex { + key, err := hex.DecodeString(publicKeyHex) + if err != nil { + t.Error(t, err) + } + hash, err := blake2b.Blake160(key) + if err != nil { + t.Error(t, err) + } + publicKeysHash = append(publicKeysHash, hash) + } + script, _, err := GenerateSecp256k1Blake160MultisigScript(0, 2, publicKeysHash) if err != nil { - return + t.Error(t, err) } - assert.Equal(t, - "ckb1qzn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvq3kcv576ccddnn4quf2ga65xee2m26h7nqe5e7m2", - address) - - t.Run("parse full address", func(t *testing.T) { - mnAddress, err := Parse(address) - - assert.Nil(t, err) - assert.Equal(t, Mainnet, mnAddress.Mode) - assert.Equal(t, FullBech32m, mnAddress.Type) - assert.Equal(t, script.CodeHash, mnAddress.Script.CodeHash) - assert.Equal(t, script.HashType, mnAddress.Script.HashType) - assert.Equal(t, script.Args, mnAddress.Script.Args) - }) - -} + address := &Address{ + Script: script, + Network: types.NetworkTest, + } + encoded, err := address.Encode() + if err != nil { + t.Error(err) + } + assert.Equal(t, "ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sq0sfnkgf0ph76pkzwld9ujzex4pkeuwnlsdc5tqu", encoded) +} \ No newline at end of file diff --git a/address/multisig_address.go b/address/multisig_address.go deleted file mode 100644 index 545d789f..00000000 --- a/address/multisig_address.go +++ /dev/null @@ -1,77 +0,0 @@ -package address - -import ( - "encoding/binary" - "errors" - - "github.com/nervosnetwork/ckb-sdk-go/crypto/blake2b" - "github.com/nervosnetwork/ckb-sdk-go/transaction" - "github.com/nervosnetwork/ckb-sdk-go/types" -) - -// GenerateSecp256k1MultisigScript generate scep256k1 multisig script. -// It can accept public key (in compressed format, 33 bytes each) array or public key hash (20 bytes) array, and -// return error if giving none of them. -func GenerateSecp256k1MultisigScript(requireN, threshold int, publicKeysOrHashes [][]byte) (*types.Script, []byte, error) { - if requireN < 0 || requireN > 255 { - return nil, nil, errors.New("requireN must ranging from 0 to 255") - } - if threshold < 0 || threshold > 255 { - return nil, nil, errors.New("requireN must ranging from 0 to 255") - } - if len(publicKeysOrHashes) > 255 { - return nil, nil, errors.New("public keys size must be less than 256") - } - if len(publicKeysOrHashes) < requireN || len(publicKeysOrHashes) < threshold { - return nil, nil, errors.New("public keys error") - } - - - - var data []byte - data = append(data, 0) - - b := make([]byte, 2) - binary.LittleEndian.PutUint16(b, uint16(requireN)) - data = append(data, b[:1]...) - - b = make([]byte, 2) - binary.LittleEndian.PutUint16(b, uint16(threshold)) - data = append(data, b[:1]...) - - b = make([]byte, 2) - binary.LittleEndian.PutUint16(b, uint16(len(publicKeysOrHashes))) - data = append(data, b[:1]...) - - isPublicKeyHash := len(publicKeysOrHashes[0]) == 20 - if isPublicKeyHash { - for _, publicKeyHash := range publicKeysOrHashes { - if len(publicKeyHash) != 20 { - return nil, nil, errors.New("public key hash length must be 20 bytes") - } - data = append(data, publicKeyHash...) - } - } else { - for _, publicKey := range publicKeysOrHashes { - if len(publicKey) != 33 { - return nil, nil, errors.New("public key (compressed) length must be 33 bytes") - } - publicKeyHash, err := blake2b.Blake160(publicKey) - if err != nil { - return nil, nil, err - } - data = append(data, publicKeyHash...) - } - } - - args, err := blake2b.Blake160(data) - if err != nil { - return nil, nil, err - } - - return &types.Script{ - CodeHash: types.HexToHash(transaction.SECP256K1_BLAKE160_MULTISIG_ALL_TYPE_HASH), - HashType: types.HashTypeType, - Args: args, - }, data, nil -} diff --git a/address/multisig_address_test.go b/address/multisig_address_test.go deleted file mode 100644 index a26423d5..00000000 --- a/address/multisig_address_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package address - -import ( - "encoding/hex" - "github.com/nervosnetwork/ckb-sdk-go/crypto/blake2b" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGenerateSecp256k1MultisigScript(t *testing.T) { - var publicKeys [][]byte - - key, err := hex.DecodeString("032edb83018b57ddeb9bcc7287c5cc5da57e6e0289d31c9e98cb361e88678d6288") - if err != nil { - assert.Error(t, err) - } - publicKeys = append(publicKeys, key) - - key, err = hex.DecodeString("033aeb3fdbfaac72e9e34c55884a401ee87115302c146dd9e314677d826375dc8f") - if err != nil { - assert.Error(t, err) - } - publicKeys = append(publicKeys, key) - - key, err = hex.DecodeString("029a685b8206550ea1b600e347f18fd6115bffe582089d3567bec7eba57d04df01") - if err != nil { - assert.Error(t, err) - } - publicKeys = append(publicKeys, key) - - script, _, err := GenerateSecp256k1MultisigScript(0, 2, publicKeys) - if err != nil { - assert.Error(t, err) - } - - address, err := ConvertScriptToAddress(Testnet, script) - if err != nil { - assert.Error(t, err) - } - assert.Equal(t, "ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sq0sfnkgf0ph76pkzwld9ujzex4pkeuwnlsdc5tqu", address) -} - -func TestGenerateSecp256k1MultisigScriptByHash(t *testing.T) { - publicKeysHex := []string { - "032edb83018b57ddeb9bcc7287c5cc5da57e6e0289d31c9e98cb361e88678d6288", - "033aeb3fdbfaac72e9e34c55884a401ee87115302c146dd9e314677d826375dc8f", - "029a685b8206550ea1b600e347f18fd6115bffe582089d3567bec7eba57d04df01", - } - var publicKeysHash [][]byte - for _, publicKeyHex := range publicKeysHex { - key, err := hex.DecodeString(publicKeyHex) - if err != nil { - assert.Error(t, err) - } - hash, err := blake2b.Blake160(key) - if err != nil { - assert.Error(t, err) - } - publicKeysHash = append(publicKeysHash, hash) - } - script, _, err := GenerateSecp256k1MultisigScript(0, 2, publicKeysHash) - if err != nil { - assert.Error(t, err) - } - - address, err := ConvertScriptToAddress(Testnet, script) - if err != nil { - assert.Error(t, err) - } - assert.Equal(t, "ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sq0sfnkgf0ph76pkzwld9ujzex4pkeuwnlsdc5tqu", address) -} diff --git a/mercury/model/item.go b/mercury/model/item.go index 3c958459..db5e06a0 100644 --- a/mercury/model/item.go +++ b/mercury/model/item.go @@ -5,6 +5,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" address2 "github.com/nervosnetwork/ckb-sdk-go/address" "github.com/nervosnetwork/ckb-sdk-go/types" + "reflect" ) const ( @@ -43,8 +44,7 @@ func NewIdentityItemByCkb(publicKeyHash string) (*Item, error) { } func NewIdentityItemByAddress(address string) (*Item, error) { - // TODO: check address type - parse, err := address2.Parse(address) + parse, err := decodeItemAddress(address) if err != nil { return nil, err } @@ -56,6 +56,23 @@ func NewIdentityItemByAddress(address string) (*Item, error) { return &Item{ItemTypeIdentity, identity}, nil } +func decodeItemAddress(address string) (*address2.Address, error) { + a, err := address2.Decode(address) + if err != nil { + return nil, err + } + ss := []types.BuiltinScript{ + types.BuiltinScriptSecp256k1Blake160SighashAll, + types.BuiltinScriptAnyoneCanPay} + for _, s := range ss { + hash := types.GetCodeHash(s, a.Network) + if reflect.DeepEqual(hash, a.Script.CodeHash) { + return a, nil + } + } + return nil, errors.New("not a valid secp256k1_blake160_signhash_all or ACP address") +} + func toIdentity(flag byte, content []byte) (string, error) { if len(content) != 20 { return "", errors.New("identity content should be 20 bytes length") diff --git a/payment/cheque_test.go b/payment/cheque_test.go index 68e50365..283d59fc 100644 --- a/payment/cheque_test.go +++ b/payment/cheque_test.go @@ -93,7 +93,7 @@ func genSearchKey(addr string) *indexer.SearchKey { } func getLock(addr string) *types.Script { - parsedAddr, _ := address.Parse(addr) + parsedAddr, _ := address.Decode(addr) return parsedAddr.Script } diff --git a/payment/payment.go b/payment/payment.go index 1b634d59..84347ba4 100644 --- a/payment/payment.go +++ b/payment/payment.go @@ -27,17 +27,17 @@ type Payment struct { // NewPayment returns a Payment object, amount's unit is shannon func NewPayment(from, to string, amount, feeRate uint64) (*Payment, error) { - fromAddress, err := address.Parse(from) + fromAddress, err := address.Decode(from) if err != nil { return nil, fmt.Errorf("parse from address %s error: %v", from, err) } - toAddress, err := address.Parse(to) + toAddress, err := address.Decode(from) if err != nil { return nil, fmt.Errorf("parse to address %s error: %v", to, err) } - if fromAddress.Mode != toAddress.Mode { - return nil, fmt.Errorf("from address and to address with diffrent network: %v:%v", fromAddress.Mode, toAddress.Mode) + if fromAddress.Network != toAddress.Network { + return nil, fmt.Errorf("from address and to address with diffrent network: %v:%v", fromAddress.Network, toAddress.Network) } return &Payment{ diff --git a/payment/sudt.go b/payment/sudt.go index 62341967..f27952a8 100644 --- a/payment/sudt.go +++ b/payment/sudt.go @@ -39,14 +39,14 @@ func NewSudt(senderAddresses []string, receiverInfo map[string]string, ckbPayerA var receivers []types.ReceiverInfo totalAmount := big.NewInt(0) for _, senderAddr := range senderAddresses { - parsedSenderAddr, err := address.Parse(senderAddr) + parsedSenderAddr, err := address.Decode(senderAddr) if err != nil { return nil, err } senders = append(senders, parsedSenderAddr.Script) } for receiverAddr, amount := range receiverInfo { - parsedReceiverAddr, err := address.Parse(receiverAddr) + parsedReceiverAddr, err := address.Decode(receiverAddr) if err != nil { return nil, err } @@ -60,15 +60,15 @@ func NewSudt(senderAddresses []string, receiverInfo map[string]string, ckbPayerA }) totalAmount = big.NewInt(0).Add(totalAmount, n) } - parsedPayFeeAddr, err := address.Parse(ckbPayerAddress) + parsedPayFeeAddr, err := address.Decode(ckbPayerAddress) if err != nil { return nil, err } - parsedCkbChangeAddr, err := address.Parse(ckbChangeAddress) + parsedCkbChangeAddr, err := address.Decode(ckbChangeAddress) if err != nil { return nil, err } - parsedSudtChangeAddr, err := address.Parse(sudtChangeAddress) + parsedSudtChangeAddr, err := address.Decode(sudtChangeAddress) if err != nil { return nil, err } diff --git a/transaction/constants.go b/transaction/constants.go deleted file mode 100644 index b8de33d6..00000000 --- a/transaction/constants.go +++ /dev/null @@ -1,7 +0,0 @@ -package transaction - -const ( - SECP256K1_BLAKE160_SIGHASH_ALL_DATA_HASH = "0x973bdb373cbb1d752b4ac006e2bb5bdcb63431ed2b6e394b22721c8906a2ad72" - SECP256K1_BLAKE160_SIGHASH_ALL_TYPE_HASH = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" - SECP256K1_BLAKE160_MULTISIG_ALL_TYPE_HASH = "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8" -) diff --git a/transaction/signer/secp256k1_blake160_multisig_all.go b/transaction/signer/secp256k1_blake160_multisig_all.go index 430b1b54..297cc2a3 100644 --- a/transaction/signer/secp256k1_blake160_multisig_all.go +++ b/transaction/signer/secp256k1_blake160_multisig_all.go @@ -77,7 +77,7 @@ func setSignatureToWitness(witness []byte, signature []byte, m *MultisigScript) return nil, err } lock := witnessArgs.Lock - pos := len(m.encode()) + pos := len(m.Encode()) emptySignature := [65]byte{} for i := 0; i < int(m.Threshold); i++ { if reflect.DeepEqual(emptySignature[:], lock[pos:pos+65]) { @@ -138,7 +138,7 @@ func (r *MultisigScript) AddKeyHashBySlice(keyHash []byte) error { return nil } -func (r *MultisigScript) encode() []byte { +func (r *MultisigScript) Encode() []byte { out := make([]byte, 4) out[0] = r.Version out[1] = r.FirstN @@ -196,16 +196,16 @@ func (r *MultisigScript) WitnessPlaceholder(originalWitness []byte) ([]byte, err } func (r *MultisigScript) WitnessPlaceholderInLock() []byte { - header := r.encode() + header := r.Encode() b := make([]byte, len(header)+65*int(r.Threshold)) copy(b[:len(header)], header) return b } func (r *MultisigScript) ComputeHash() ([]byte, error) { - hash, err := blake2b.Blake160(r.encode()[:]) + hash, err := blake2b.Blake160(r.Encode()[:]) if err != nil { return nil, err } return hash, nil -} +} \ No newline at end of file diff --git a/transaction/signer/signer.go b/transaction/signer/signer.go index 24bb656c..ebf295d2 100644 --- a/transaction/signer/signer.go +++ b/transaction/signer/signer.go @@ -81,9 +81,17 @@ func hash(codeHash types.Hash, scriptType transaction.ScriptType) (types.Hash, e return types.BytesToHash(hash), nil } -func (r *TransactionSigner) SignTransaction(transaction *transaction.TransactionWithScriptGroups, contexts transaction.Contexts) ([]int, error) { +func (r *TransactionSigner) SignTransactionByPrivateKeys(tx *transaction.TransactionWithScriptGroups, privKeys ...string) ([]int, error) { + ctxs := transaction.NewContexts() + if err := ctxs.AddByPrivateKeys(privKeys...); err != nil { + return nil, err + } + return r.SignTransaction(tx, ctxs) +} + +func (r *TransactionSigner) SignTransaction(tx *transaction.TransactionWithScriptGroups, contexts transaction.Contexts) ([]int, error) { signedIndex := make([]int, 0) - for i, group := range transaction.ScriptGroups { + for i, group := range tx.ScriptGroups { if err := checkScriptGroup(group); err != nil { return signedIndex, err } @@ -95,7 +103,7 @@ func (r *TransactionSigner) SignTransaction(transaction *transaction.Transaction if signer != nil { for _, ctx := range contexts { var signed bool - if signed, err = signer.SignTransaction(transaction.TxView, group, ctx); err != nil { + if signed, err = signer.SignTransaction(tx.TxView, group, ctx); err != nil { return signedIndex, err } if signed { @@ -125,4 +133,4 @@ func checkScriptGroup(group *transaction.ScriptGroup) error { return errors.New("unknown group type " + string(group.GroupType)) } return nil -} +} \ No newline at end of file diff --git a/transaction/signer/signer_test.go b/transaction/signer/signer_test.go index 4a5892ff..3f43c3ea 100644 --- a/transaction/signer/signer_test.go +++ b/transaction/signer/signer_test.go @@ -77,7 +77,7 @@ func TestMultiScriptEncode(t *testing.T) { Threshold: 2, KeysHashes: getKeysHashes(), } - encoded := m.encode() + encoded := m.Encode() assert.Equal(t, common.FromHex("0x000002029b41c025515b00c24e2e2042df7b221af5c1891fe732dcd15b7618eb1d7a11e6a68e4579b5be0114"), encoded) hash, err := m.ComputeHash() if err != nil { @@ -197,4 +197,4 @@ func (r *signerChecker) UnmarshalJSON(input []byte) error { r.Contexts.Add(ctx) } return nil -} +} \ No newline at end of file diff --git a/types/serialize_types.go b/types/serialize_types.go index ad47d01b..84dd11b1 100644 --- a/types/serialize_types.go +++ b/types/serialize_types.go @@ -3,6 +3,7 @@ package types import ( "bytes" "errors" + "fmt" ) func (h Hash) Serialize() ([]byte, error) { @@ -199,6 +200,19 @@ func SerializeHashType(hashType ScriptHashType) (string, error) { return "", errors.New("Invalid script hash_type: " + string(hashType)) } +func SerializeHashTypeByte(hashType ScriptHashType) (byte, error) { + switch hashType { + case HashTypeData: + return 0x00, nil + case HashTypeType: + return 0x01, nil + case HashTypeData1: + return 0x02, nil + default: + return 0, errors.New(string("unknown hash type " + hashType)) + } +} + func DeserializeHashType(hashType string) (ScriptHashType, error) { if "00" == hashType { return HashTypeData, nil @@ -210,3 +224,16 @@ func DeserializeHashType(hashType string) (ScriptHashType, error) { return "", errors.New("Invalid script hash_type: " + hashType) } + +func DeserializeHashTypeByte(hashType byte) (ScriptHashType, error) { + switch hashType { + case 0x00: + return HashTypeData, nil + case 0x01: + return HashTypeType, nil + case 0x02: + return HashTypeData1, nil + default: + return "", errors.New(fmt.Sprintf("invalid script hash_type: %x", hashType)) + } +}