near / borsh-go

Go implementation of Binary Object Representation Serializer for Hashing
The Unlicense
50 stars 16 forks source link

关于borsh-go不支持big.int的问题 #10

Closed lijianzhong897 closed 2 years ago

lijianzhong897 commented 2 years ago

在进行near-go语言化时,转账必须使用big.int,目前的uint都会发生溢出。经过我几天的努力也解决了这个问题,下面提供一个可行的思路,以及转账使用的结构体(时间和工作原因,我只构建了转账方法的结构体)。 1.支持big.int序列和反序列化 (1)serialize 对borsh.serialize 进行修改: case reflect.Ptr: if v.Type() == reflect.TypeOf(big.NewInt(0)) { err = serializeUint128ByPtr(v, b) } else if v.IsNil() { , err = b.Write([]byte{0}) } else { , err = b.Write([]byte{1}) if err != nil { break } err = serialize(v.Elem(), b) }

serializeUint128ByPtr: func serializeUint128ByPtr(v reflect.Value, b io.Writer) error { u := v.Interface().(*big.Int) buf := u.Bytes() if len(buf) > 16 { return errors.New("big.Int too large for u128") } // fill big-endian buffer var d [16]byte copy(d[16-len(buf):], buf) // make it little-endian for i, j := 0, 15; i < j; i, j = i+1, j-1 { d[i], d[j] = d[j], d[i] } _, err := b.Write(d[:]) return err } (2)deserialize 对deserialize进行修改: case reflect.Ptr: if t == reflect.TypeOf(big.NewInt(0)) { s, err := deserializeUint128ByPtr(t, r) if err != nil { return nil, err } return s, nil } tmp, err := read(r, 1) if err != nil { return nil, err } valid := uint8(tmp[0]) if valid == 0 { p := reflect.New(t.Elem()) return p.Interface(), nil } else { p := reflect.New(t.Elem()) de, err := deserialize(t.Elem(), r) if err != nil { return nil, err } p.Elem().Set(reflect.ValueOf(de)) return p.Interface(), nil } deserializeUint128ByPtr: func deserializeUint128ByPtr(t reflect.Type, r io.Reader) (interface{}, error) { d, err := read(r, 16) if err != nil { return nil, err } // make it big-endian for i, j := 0, 15; i < j; i, j = i+1, j-1 { d[i], d[j] = d[j], d[i] } var u big.Int u.SetBytes(d[:]) return &u, nil }

问:为什么要这么修改?答:go在执行u := v.Interface().(big.Int) 会报错,因为big.int只能通过指针进行传值。具体原因可以查看big.int包。 测试: 在borsh_test中添加: func TestBigInt128(t testing.T) { var f F f.A = 3 f.B = big.NewInt(1234) data, err := Serialize(f) if err != nil { t.Error(err) } g := new(F) err = Deserialize(g, data) if err != nil { t.Error(err) } if !reflect.DeepEqual(f, g) { t.Error(f, g) } }

附:可行的交易结构体 type SignedTransaction struct { transaction Transaction signature Signature } type Signature struct { keyType uint8 data [64]byte }

type Transaction struct { signerId string publicKey struct { keyType uint8 data [32]byte } nonce uint64 receiverId string blockHash [32]byte actions []Action } type Action struct { Enum borsh.Enum borsh_enum:"true" createAccount CreateAccount deployContract DeployContract functionCall FunctionCall transfer Transfer stake Stake addKey AddKey deleteKey DeleteKey deleteAccount DeleteAccount } type CreateAccount struct { } type DeployContract struct { } type FunctionCall struct { } type Transfer struct { deposit big.Int } type Stake struct { } type AddKey struct { } type DeleteKey struct { } type DeleteAccount struct { }

frol commented 2 years ago

I don't understand you language. Please, update the title and description so they are in English and format the code so it is readable, and then re-open this issue 🙏