From 326d04a5e4ebe5b500684db45f10abe4468e21d4 Mon Sep 17 00:00:00 2001 From: Hang Su <87964331+ahangsu@users.noreply.github.com> Date: Thu, 13 Jan 2022 14:02:06 -0500 Subject: Removing C/crypto dependencies from `data/abi` package (#3375) --- data/abi/abi_encode.go | 16 ++--- data/abi/abi_encode_test.go | 159 +++++++++++++++++++++----------------------- data/abi/abi_json.go | 57 +++++++++++++--- data/abi/abi_json_test.go | 27 ++++++++ data/abi/abi_type.go | 16 +++-- data/basics/address.go | 17 +++++ 6 files changed, 185 insertions(+), 107 deletions(-) diff --git a/data/abi/abi_encode.go b/data/abi/abi_encode.go index 1f14af71a..8e5a49086 100644 --- a/data/abi/abi_encode.go +++ b/data/abi/abi_encode.go @@ -170,14 +170,14 @@ func encodeInt(intValue interface{}, bitSize uint16) ([]byte, error) { return nil, fmt.Errorf("passed in numeric value should be non negative") } - bytes := bigInt.Bytes() - if len(bytes) > int(bitSize/8) { - return nil, fmt.Errorf("input value bit size %d > abi type bit size %d", len(bytes)*8, bitSize) + castedBytes := make([]byte, bitSize/8) + + if bigInt.Cmp(new(big.Int).Lsh(big.NewInt(1), uint(bitSize))) >= 0 { + return nil, fmt.Errorf("input value bit size %d > abi type bit size %d", bigInt.BitLen(), bitSize) } - zeroPadding := make([]byte, bitSize/8-uint16(len(bytes))) - buffer := append(zeroPadding, bytes...) - return buffer, nil + bigInt.FillBytes(castedBytes) + return castedBytes, nil } // inferToSlice infers an interface element to a slice of interface{}, returns error if it cannot infer successfully @@ -201,7 +201,7 @@ func inferToSlice(value interface{}) ([]interface{}, error) { // encodeTuple encodes slice-of-interface of golang values to bytes, following ABI encoding rules func encodeTuple(value interface{}, childT []Type) ([]byte, error) { - if len(childT) >= (1 << 16) { + if len(childT) >= abiEncodingLengthLimit { return nil, fmt.Errorf("abi child type number exceeds uint16 maximum") } values, err := inferToSlice(value) @@ -277,7 +277,7 @@ func encodeTuple(value interface{}, childT []Type) ([]byte, error) { if isDynamicIndex[i] { // calculate where the index of dynamic value encoding byte start headValue := headLength + tailCurrLength - if headValue >= (1 << 16) { + if headValue >= abiEncodingLengthLimit { return nil, fmt.Errorf("cannot encode abi tuple: encode length exceeds uint16 maximum") } binary.BigEndian.PutUint16(heads[i], uint16(headValue)) diff --git a/data/abi/abi_encode_test.go b/data/abi/abi_encode_test.go index 64296680b..66060f5ce 100644 --- a/data/abi/abi_encode_test.go +++ b/data/abi/abi_encode_test.go @@ -29,21 +29,21 @@ import ( ) const ( - UintStepLength = 8 - UintBegin = 8 - UintEnd = 512 - UintRandomTestPoints = 1000 - UintTestCaseCount = 200 - UfixedPrecision = 160 - UfixedRandomTestPoints = 20 - TupleMaxLength = 10 - ByteTestCaseCount = 1 << 8 - BoolTestCaseCount = 2 - AddressTestCaseCount = 300 - StringTestCaseCount = 10 - StringTestCaseSpecLenCount = 5 - TakeNum = 10 - TupleTestCaseCount = 100 + uintStepLength = 8 + uintBegin = 8 + uintEnd = 512 + uintRandomTestPoints = 1000 + uintTestCaseCount = 200 + ufixedPrecision = 160 + ufixedRandomTestPoints = 20 + tupleMaxLength = 10 + byteTestCaseCount = 1 << 8 + boolTestCaseCount = 2 + addressTestCaseCount = 300 + stringTestCaseCount = 10 + stringTestCaseSpecLenCount = 5 + takeNum = 10 + tupleTestCaseCount = 100 ) /* @@ -74,18 +74,17 @@ func TestEncodeValid(t *testing.T) { // encoding test for uint type, iterating through all uint sizes // randomly pick 1000 valid uint values and check if encoded value match with expected - for intSize := UintBegin; intSize <= UintEnd; intSize += UintStepLength { - upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(intSize)) + for intSize := uintBegin; intSize <= uintEnd; intSize += uintStepLength { + upperLimit := new(big.Int).Lsh(big.NewInt(1), uint(intSize)) uintType, err := makeUintType(intSize) require.NoError(t, err, "make uint type fail") - for i := 0; i < UintRandomTestPoints; i++ { + for i := 0; i < uintRandomTestPoints; i++ { randomInt, err := rand.Int(rand.Reader, upperLimit) require.NoError(t, err, "cryptographic random int init fail") - randomIntByte := randomInt.Bytes() - expected := make([]byte, intSize/8-len(randomIntByte)) - expected = append(expected, randomIntByte...) + expected := make([]byte, intSize/8) + randomInt.FillBytes(expected) uintEncode, err := uintType.Encode(randomInt) require.NoError(t, err, "encoding from uint type fail") @@ -94,9 +93,9 @@ func TestEncodeValid(t *testing.T) { } // 2^[bitSize] - 1 test // check if uint can contain max uint value (2^bitSize - 1) - largest := big.NewInt(0).Add( + largest := new(big.Int).Add( upperLimit, - big.NewInt(1).Neg(big.NewInt(1)), + new(big.Int).Neg(big.NewInt(1)), ) encoded, err := uintType.Encode(largest) require.NoError(t, err, "largest uint encode error") @@ -106,27 +105,26 @@ func TestEncodeValid(t *testing.T) { // encoding test for ufixed, iterating through all the valid ufixed bitSize and precision // randomly generate 10 big int values for ufixed numerator and check if encoded value match with expected // also check if ufixed can fit max numerator (2^bitSize - 1) under specific byte bitSize - for size := UintBegin; size <= UintEnd; size += UintStepLength { - upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(size)) + for size := uintBegin; size <= uintEnd; size += uintStepLength { + upperLimit := new(big.Int).Lsh(big.NewInt(1), uint(size)) largest := big.NewInt(0).Add( upperLimit, - big.NewInt(1).Neg(big.NewInt(1)), + new(big.Int).Neg(big.NewInt(1)), ) - for precision := 1; precision <= UfixedPrecision; precision++ { + for precision := 1; precision <= ufixedPrecision; precision++ { typeUfixed, err := makeUfixedType(size, precision) require.NoError(t, err, "make ufixed type fail") - for i := 0; i < UfixedRandomTestPoints; i++ { + for i := 0; i < ufixedRandomTestPoints; i++ { randomInt, err := rand.Int(rand.Reader, upperLimit) require.NoError(t, err, "cryptographic random int init fail") encodedUfixed, err := typeUfixed.Encode(randomInt) require.NoError(t, err, "ufixed encode fail") - randomBytes := randomInt.Bytes() - buffer := make([]byte, size/8-len(randomBytes)) - buffer = append(buffer, randomBytes...) - require.Equal(t, buffer, encodedUfixed, "encode ufixed not match with expected") + expected := make([]byte, size/8) + randomInt.FillBytes(expected) + require.Equal(t, expected, encodedUfixed, "encode ufixed not match with expected") } // (2^[bitSize] - 1) / (10^[precision]) test ufixedLargestEncode, err := typeUfixed.Encode(largest) @@ -138,14 +136,13 @@ func TestEncodeValid(t *testing.T) { // encoding test for address, since address is 32 byte, it can be considered as 256 bit uint // randomly generate 1000 uint256 and make address values, check if encoded value match with expected - upperLimit := big.NewInt(0).Lsh(big.NewInt(1), addressByteSize<<3) - for i := 0; i < UintRandomTestPoints; i++ { + upperLimit := new(big.Int).Lsh(big.NewInt(1), addressByteSize<<3) + for i := 0; i < uintRandomTestPoints; i++ { randomAddrInt, err := rand.Int(rand.Reader, upperLimit) require.NoError(t, err, "cryptographic random int init fail") - rand256Bytes := randomAddrInt.Bytes() - addrBytesExpected := make([]byte, addressByteSize-len(rand256Bytes)) - addrBytesExpected = append(addrBytesExpected, rand256Bytes...) + addrBytesExpected := make([]byte, addressByteSize) + randomAddrInt.FillBytes(addrBytesExpected) addrBytesActual, err := addressType.Encode(addrBytesExpected) require.NoError(t, err, "address encode fail") @@ -153,7 +150,7 @@ func TestEncodeValid(t *testing.T) { } // encoding test for bool values - for i := 0; i < BoolTestCaseCount; i++ { + for i := 0; i < boolTestCaseCount; i++ { boolEncode, err := boolType.Encode(i == 1) require.NoError(t, err, "bool encode fail") expected := []byte{0x00} @@ -164,7 +161,7 @@ func TestEncodeValid(t *testing.T) { } // encoding test for byte values - for i := 0; i < ByteTestCaseCount; i++ { + for i := 0; i < byteTestCaseCount; i++ { byteEncode, err := byteType.Encode(byte(i)) require.NoError(t, err, "byte encode fail") expected := []byte{byte(i)} @@ -175,8 +172,8 @@ func TestEncodeValid(t *testing.T) { // we use `gobberish` to generate random utf-8 symbols // randomly generate utf-8 str from length 1 to 100, each length draw 10 random strs // check if encoded ABI str match with expected value - for length := 1; length <= StringTestCaseCount; length++ { - for i := 0; i < StringTestCaseSpecLenCount; i++ { + for length := 1; length <= stringTestCaseCount; length++ { + for i := 0; i < stringTestCaseSpecLenCount; i++ { // generate utf8 strings from `gobberish` at some length utf8Str := gobberish.GenerateString(length) // since string is just type alias of `byte[]`, we need to store number of bytes in encoding @@ -350,11 +347,11 @@ func TestDecodeValid(t *testing.T) { // decoding test for uint, iterating through all valid uint bitSize // randomly take 1000 tests on each valid bitSize // generate bytes from random uint values and decode bytes with additional type information - for intSize := 8; intSize <= 512; intSize += 8 { - upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(intSize)) + for intSize := uintBegin; intSize <= uintEnd; intSize += uintStepLength { + upperLimit := new(big.Int).Lsh(big.NewInt(1), uint(intSize)) uintType, err := makeUintType(intSize) require.NoError(t, err, "make uint type failure") - for i := 0; i < 1000; i++ { + for i := 0; i < uintRandomTestPoints; i++ { randBig, err := rand.Int(rand.Reader, upperLimit) require.NoError(t, err, "cryptographic random int init fail") @@ -383,12 +380,12 @@ func TestDecodeValid(t *testing.T) { // decoding test for ufixed, iterating through all valid ufixed bitSize and precision // randomly take 10 tests on each valid setting // generate ufixed bytes and try to decode back with additional type information - for size := 8; size <= 512; size += 8 { + for size := uintBegin; size <= uintEnd; size += uintStepLength { upperLimit := big.NewInt(0).Lsh(big.NewInt(1), uint(size)) - for precision := 1; precision <= 160; precision++ { + for precision := 1; precision <= ufixedPrecision; precision++ { ufixedType, err := makeUfixedType(size, precision) require.NoError(t, err, "make ufixed type failure") - for i := 0; i < 10; i++ { + for i := 0; i < ufixedRandomTestPoints; i++ { randBig, err := rand.Int(rand.Reader, upperLimit) require.NoError(t, err, "cryptographic random int init fail") @@ -416,17 +413,16 @@ func TestDecodeValid(t *testing.T) { } } - // decoding test for address, randomly take 1000 tests + // decoding test for address, randomly take 300 tests // address is type alias of byte[32], we generate address value with random 256 bit big int values // we make the expected address value and decode the encoding of expected, check if they match - upperLimit := big.NewInt(0).Lsh(big.NewInt(1), 256) - for i := 0; i < 1000; i++ { + upperLimit := new(big.Int).Lsh(big.NewInt(1), addressByteSize<<3) + for i := 0; i < addressTestCaseCount; i++ { randomAddrInt, err := rand.Int(rand.Reader, upperLimit) require.NoError(t, err, "cryptographic random int init fail") - addressBytes := randomAddrInt.Bytes() - expected := make([]byte, 32-len(addressBytes)) - expected = append(expected, addressBytes...) + expected := make([]byte, addressByteSize) + randomAddrInt.FillBytes(expected) actual, err := addressType.Decode(expected) require.NoError(t, err, "decoding address should not return error") @@ -443,7 +439,7 @@ func TestDecodeValid(t *testing.T) { } // byte value decoding test, iterating through 256 valid byte value - for i := 0; i < (1 << 8); i++ { + for i := 0; i < byteTestCaseCount; i++ { byteEncode, err := byteType.Encode(byte(i)) require.NoError(t, err, "byte encode fail") actual, err := byteType.Decode(byteEncode) @@ -451,11 +447,11 @@ func TestDecodeValid(t *testing.T) { require.Equal(t, byte(i), actual, "decode byte not match with expected") } - // string value decoding test, test from utf string length 1 to 100 - // randomly take 10 utf-8 strings to make ABI string values + // string value decoding test, test from utf string length 1 to 10 + // randomly take 5 utf-8 strings to make ABI string values // decode the encoded expected value and check if they match - for length := 1; length <= 100; length++ { - for i := 0; i < 10; i++ { + for length := 1; length <= stringTestCaseCount; length++ { + for i := 0; i < stringTestCaseSpecLenCount; i++ { expected := gobberish.GenerateString(length) strEncode, err := stringType.Encode(expected) require.NoError(t, err, "string encode fail") @@ -898,20 +894,20 @@ func categorySelfRoundTripTest(t *testing.T, category []testUnit) { } func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) { - (*pool)[Uint] = make([]testUnit, UintTestCaseCount*UintEnd/UintStepLength) - (*pool)[Ufixed] = make([]testUnit, UfixedPrecision*UintEnd/UintStepLength) + (*pool)[Uint] = make([]testUnit, uintTestCaseCount*uintEnd/uintStepLength) + (*pool)[Ufixed] = make([]testUnit, ufixedPrecision*uintEnd/uintStepLength) uintIndex := 0 ufixedIndex := 0 - for bitSize := UintBegin; bitSize <= UintEnd; bitSize += UintStepLength { + for bitSize := uintBegin; bitSize <= uintEnd; bitSize += uintStepLength { max := new(big.Int).Lsh(big.NewInt(1), uint(bitSize)) uintT, err := makeUintType(bitSize) require.NoError(t, err, "make uint type failure") uintTstr := uintT.String() - for j := 0; j < UintTestCaseCount; j++ { + for j := 0; j < uintTestCaseCount; j++ { randVal, err := rand.Int(rand.Reader, max) require.NoError(t, err, "generate random uint, should be no error") @@ -922,7 +918,7 @@ func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) { uintIndex++ } - for precision := 1; precision <= UfixedPrecision; precision++ { + for precision := 1; precision <= ufixedPrecision; precision++ { randVal, err := rand.Int(rand.Reader, max) require.NoError(t, err, "generate random ufixed, should be no error") @@ -939,33 +935,32 @@ func addPrimitiveRandomValues(t *testing.T, pool *map[BaseType][]testUnit) { categorySelfRoundTripTest(t, (*pool)[Uint]) categorySelfRoundTripTest(t, (*pool)[Ufixed]) - (*pool)[Byte] = make([]testUnit, ByteTestCaseCount) - for i := 0; i < ByteTestCaseCount; i++ { + (*pool)[Byte] = make([]testUnit, byteTestCaseCount) + for i := 0; i < byteTestCaseCount; i++ { (*pool)[Byte][i] = testUnit{serializedType: byteType.String(), value: byte(i)} } categorySelfRoundTripTest(t, (*pool)[Byte]) - (*pool)[Bool] = make([]testUnit, BoolTestCaseCount) + (*pool)[Bool] = make([]testUnit, boolTestCaseCount) (*pool)[Bool][0] = testUnit{serializedType: boolType.String(), value: false} (*pool)[Bool][1] = testUnit{serializedType: boolType.String(), value: true} categorySelfRoundTripTest(t, (*pool)[Bool]) maxAddress := new(big.Int).Lsh(big.NewInt(1), addressByteSize<<3) - (*pool)[Address] = make([]testUnit, AddressTestCaseCount) - for i := 0; i < AddressTestCaseCount; i++ { + (*pool)[Address] = make([]testUnit, addressTestCaseCount) + for i := 0; i < addressTestCaseCount; i++ { randAddrVal, err := rand.Int(rand.Reader, maxAddress) require.NoError(t, err, "generate random value for address, should be no error") - addrBytes := randAddrVal.Bytes() - remainBytes := make([]byte, addressByteSize-len(addrBytes)) - addrBytes = append(remainBytes, addrBytes...) + addrBytes := make([]byte, addressByteSize) + randAddrVal.FillBytes(addrBytes) (*pool)[Address][i] = testUnit{serializedType: addressType.String(), value: addrBytes} } categorySelfRoundTripTest(t, (*pool)[Address]) - (*pool)[String] = make([]testUnit, StringTestCaseCount*StringTestCaseSpecLenCount) + (*pool)[String] = make([]testUnit, stringTestCaseCount*stringTestCaseSpecLenCount) stringIndex := 0 - for length := 1; length <= StringTestCaseCount; length++ { - for i := 0; i < StringTestCaseSpecLenCount; i++ { + for length := 1; length <= stringTestCaseCount; length++ { + for i := 0; i < stringTestCaseSpecLenCount; i++ { (*pool)[String][stringIndex] = testUnit{ serializedType: stringType.String(), value: gobberish.GenerateString(length), @@ -1000,21 +995,21 @@ func takeSomeFromCategoryAndGenerateArray( } func addArrayRandomValues(t *testing.T, pool *map[BaseType][]testUnit) { - for intIndex := 0; intIndex < len((*pool)[Uint]); intIndex += UintTestCaseCount { - takeSomeFromCategoryAndGenerateArray(t, Uint, intIndex, TakeNum, pool) + for intIndex := 0; intIndex < len((*pool)[Uint]); intIndex += uintTestCaseCount { + takeSomeFromCategoryAndGenerateArray(t, Uint, intIndex, takeNum, pool) } - takeSomeFromCategoryAndGenerateArray(t, Byte, 0, TakeNum, pool) - takeSomeFromCategoryAndGenerateArray(t, Address, 0, TakeNum, pool) - takeSomeFromCategoryAndGenerateArray(t, String, 0, TakeNum, pool) - takeSomeFromCategoryAndGenerateArray(t, Bool, 0, TakeNum, pool) + takeSomeFromCategoryAndGenerateArray(t, Byte, 0, takeNum, pool) + takeSomeFromCategoryAndGenerateArray(t, Address, 0, takeNum, pool) + takeSomeFromCategoryAndGenerateArray(t, String, 0, takeNum, pool) + takeSomeFromCategoryAndGenerateArray(t, Bool, 0, takeNum, pool) categorySelfRoundTripTest(t, (*pool)[ArrayStatic]) categorySelfRoundTripTest(t, (*pool)[ArrayDynamic]) } func addTupleRandomValues(t *testing.T, slotRange BaseType, pool *map[BaseType][]testUnit) { - for i := 0; i < TupleTestCaseCount; i++ { - tupleLenBig, err := rand.Int(rand.Reader, big.NewInt(TupleMaxLength)) + for i := 0; i < tupleTestCaseCount; i++ { + tupleLenBig, err := rand.Int(rand.Reader, big.NewInt(tupleMaxLength)) require.NoError(t, err, "generate random tuple length should not return error") tupleLen := tupleLenBig.Int64() + 1 testUnits := make([]testUnit, tupleLen) diff --git a/data/abi/abi_json.go b/data/abi/abi_json.go index a3177c4b2..a71823f0c 100644 --- a/data/abi/abi_json.go +++ b/data/abi/abi_json.go @@ -18,12 +18,26 @@ package abi import ( "bytes" + "crypto/sha512" + "encoding/base32" "encoding/json" "fmt" - "github.com/algorand/go-algorand/data/basics" "math/big" ) +// NOTE: discussion about go-algorand-sdk +// https://github.com/algorand/go-algorand/pull/3375#issuecomment-1007536841 + +var base32Encoder = base32.StdEncoding.WithPadding(base32.NoPadding) + +func addressCheckSum(addressBytes []byte) ([]byte, error) { + if len(addressBytes) != addressByteSize { + return nil, fmt.Errorf("address bytes should be of length 32") + } + hashed := sha512.Sum512_256(addressBytes[:]) + return hashed[addressByteSize-checksumByteSize:], nil +} + func castBigIntToNearestPrimitive(num *big.Int, bitSize uint16) (interface{}, error) { if num.BitLen() > int(bitSize) { return nil, fmt.Errorf("cast big int to nearest primitive failure: %v >= 2^%d", num, bitSize) @@ -74,17 +88,24 @@ func (t Type) MarshalToJSON(value interface{}) ([]byte, error) { } return json.Marshal(byteValue) case Address: - var addressInternal basics.Address + var addressValueInternal []byte switch valueCasted := value.(type) { case []byte: - copy(addressInternal[:], valueCasted[:]) - return json.Marshal(addressInternal.String()) + if len(valueCasted) != addressByteSize { + return nil, fmt.Errorf("address byte slice length not equal to 32 byte") + } + addressValueInternal = valueCasted case [addressByteSize]byte: - addressInternal = valueCasted - return json.Marshal(addressInternal.String()) + copy(addressValueInternal[:], valueCasted[:]) default: return nil, fmt.Errorf("cannot infer to byte slice/array for marshal to JSON") } + checksum, err := addressCheckSum(addressValueInternal) + if err != nil { + return nil, err + } + addressValueInternal = append(addressValueInternal, checksum...) + return json.Marshal(base32Encoder.EncodeToString(addressValueInternal)) case ArrayStatic, ArrayDynamic: values, err := inferToSlice(value) if err != nil { @@ -175,13 +196,29 @@ func (t Type) UnmarshalFromJSON(jsonEncoded []byte) (interface{}, error) { case Address: var addrStr string if err := json.Unmarshal(jsonEncoded, &addrStr); err != nil { - return nil, fmt.Errorf("cannot cast JSON encoded to string: %v", err) + return nil, fmt.Errorf("cannot cast JSON encoded to address string: %v", err) } - addr, err := basics.UnmarshalChecksumAddress(addrStr) + decoded, err := base32Encoder.DecodeString(addrStr) if err != nil { - return nil, fmt.Errorf("cannot cast JSON encoded (%s) to address: %v", string(jsonEncoded), err) + return nil, + fmt.Errorf("cannot cast JSON encoded address string (%s) to address: %v", addrStr, err) + } + if len(decoded) != addressByteSize+checksumByteSize { + return nil, + fmt.Errorf( + "cannot cast JSON encoded address string (%s) to address: "+ + "decoded byte length should equal to 36 with address and checksum", + string(jsonEncoded), + ) + } + checksum, err := addressCheckSum(decoded[:addressByteSize]) + if err != nil { + return nil, err + } + if !bytes.Equal(checksum, decoded[addressByteSize:]) { + return nil, fmt.Errorf("cannot cast JSON encoded address string (%s) to address: decoded checksum unmatch", addrStr) } - return addr[:], nil + return decoded[:addressByteSize], nil case ArrayStatic, ArrayDynamic: if t.childTypes[0].abiTypeID == Byte && bytes.HasPrefix(jsonEncoded, []byte{'"'}) { var byteArr []byte diff --git a/data/abi/abi_json_test.go b/data/abi/abi_json_test.go index b5290c9d5..49083fdea 100644 --- a/data/abi/abi_json_test.go +++ b/data/abi/abi_json_test.go @@ -17,12 +17,39 @@ package abi import ( + "crypto/rand" + "math/big" "testing" + "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" ) +func TestRandomAddressEquality(t *testing.T) { + partitiontest.PartitionTest(t) + + upperLimit := new(big.Int).Lsh(big.NewInt(1), addressByteSize<<3) + var addrBasics basics.Address + var addrABI []byte = make([]byte, addressByteSize) + + for testCaseIndex := 0; testCaseIndex < addressTestCaseCount; testCaseIndex++ { + randomAddrInt, err := rand.Int(rand.Reader, upperLimit) + require.NoError(t, err, "cryptographic random int init fail") + + randomAddrInt.FillBytes(addrBasics[:]) + randomAddrInt.FillBytes(addrABI) + + checkSumBasics := addrBasics.GetChecksum() + checkSumABI, err := addressCheckSum(addrABI) + require.NoError(t, err, "ABI compute checksum for address slice failed") + + require.Equal(t, checkSumBasics, checkSumABI, + "basics.Address computed checksum %v not equal to data.abi computed checksum %v", + ) + } +} + func TestJSONtoInterfaceValid(t *testing.T) { partitiontest.PartitionTest(t) var testCases = []struct { diff --git a/data/abi/abi_type.go b/data/abi/abi_type.go index 0ff240950..f403916b2 100644 --- a/data/abi/abi_type.go +++ b/data/abi/abi_type.go @@ -60,6 +60,15 @@ const ( Tuple ) +const ( + addressByteSize = 32 + checksumByteSize = 4 + singleByteSize = 1 + singleBoolSize = 1 + lengthEncodeByteSize = 2 + abiEncodingLengthLimit = 1 << 16 +) + // Type is the struct that stores information about an ABI value's type. type Type struct { abiTypeID BaseType @@ -405,13 +414,6 @@ func findBoolLR(typeList []Type, index int, delta int) int { return until } -const ( - addressByteSize = 32 - singleByteSize = 1 - singleBoolSize = 1 - lengthEncodeByteSize = 2 -) - // ByteLen method calculates the byte length of a static ABI type. func (t Type) ByteLen() (int, error) { switch t.abiTypeID { diff --git a/data/basics/address.go b/data/basics/address.go index 412b7bf75..5eed1c512 100644 --- a/data/basics/address.go +++ b/data/basics/address.go @@ -24,6 +24,23 @@ import ( "github.com/algorand/go-algorand/crypto" ) +// NOTE: Another (partial) implementation of `basics.Address` is in `data/abi`. +// The reason of not using this `Address` in `data/abi` is that: +// - `data/basics` has C dependencies (`go-algorand/crypto`) +// - `go-algorand-sdk` has dependency to `go-algorand` for `ABI` +// - if `go-algorand`'s ABI uses `basics.Address`, then it would be +// impossible to up the version of `go-algorand` in `go-algorand-sdk` + +// This is discussed in: +// - ISSUE https://github.com/algorand/go-algorand/issues/3355 +// - PR https://github.com/algorand/go-algorand/pull/3375 + +// There are two solutions: +// - One is to refactoring `crypto.Digest`, `crypto.Hash` and `basics.Address` +// into packages that does not need `libsodium` crypto dependency +// - The other is wrapping `libsodium` in a driver interface to make crypto +// package importable (even if `libsodium` does not exist) + type ( // Address is a unique identifier corresponding to ownership of money Address crypto.Digest -- cgit v1.2.3