Skip to content

Commit

Permalink
Fix error conversion (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
gagliardetto authored Nov 14, 2024
1 parent 797ed6c commit 4808f83
Show file tree
Hide file tree
Showing 2 changed files with 493 additions and 84 deletions.
255 changes: 171 additions & 84 deletions solana-errors/from-json-to-protobuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,108 +19,200 @@ func FromJSONToProtobuf(j map[string]interface{}) ([]byte, error) {
doer := &ChainOps{}
switch firstKey {
case InstructionError:
doer.Do("write transactionErrorType", func() error {
return wr.WriteUint32(uint32(TransactionErrorType_INSTRUCTION_ERROR), bin.LE)
})

{
// read instructionErrorType
arr, ok := j[InstructionError].([]interface{})
if !ok {
return nil, fmt.Errorf("expected an array")
}
if len(arr) != 2 {
return nil, fmt.Errorf("expected an array of length 2")
}
instructionErrorCodeFloat, ok := arr[0].(float64)
if !ok {
return nil, fmt.Errorf("expected a float64, got %T", arr[0])
}

instructionErrorCode := uint8(instructionErrorCodeFloat)
doer.Do("write errorCode", func() error {
return wr.WriteUint8(instructionErrorCode)
doer.Do("write transactionErrorType", func() error {
return wr.WriteUint32(uint32(TransactionErrorType_INSTRUCTION_ERROR), bin.LE)
})

{
switch as := arr[1].(type) {
case string:
{
// if string, then map instructionErrorTypeName to code
ixLoop:
for k, v := range InstructionErrorType_name {
// TODO: the conversion to PascalCase might be wrong and break things.
if bin.ToPascalCase(v) == as {
doer.Do("write instructionErrorType", func() error {
return wr.WriteUint32(uint32(k), bin.LE)
})
break ixLoop
// read instructionErrorType
arr, ok := j[InstructionError].([]interface{})
if !ok {
return nil, fmt.Errorf("expected an array")
}
if len(arr) != 2 {
return nil, fmt.Errorf("expected an array of length 2")
}
instructionErrorCodeFloat, ok := arr[0].(float64)
if !ok {
return nil, fmt.Errorf("expected a float64, got %T", arr[0])
}

instructionErrorCode := uint8(instructionErrorCodeFloat)
doer.Do("write errorCode", func() error {
return wr.WriteUint8(instructionErrorCode)
})

{
switch as := arr[1].(type) {
case string:
{
var found bool
// if string, then map instructionErrorTypeName to code
ixLoop:
for k, v := range InstructionErrorType_name {
// TODO: the conversion to PascalCase might be wrong and break things.
if bin.ToPascalCase(v) == as {
doer.Do("write instructionErrorType", func() error {
return wr.WriteUint32(uint32(k), bin.LE)
})
found = true
break ixLoop
}
}
if !found {
return nil, fmt.Errorf("unknown error type: %q", as)
}
}
}
case map[string]interface{}:
{
// if object, then it's custom
firstKey := getFirstKey(as)
if firstKey == "" {
return nil, fmt.Errorf("no keys found in map")
}
if firstKey != "Custom" {
return nil, fmt.Errorf("expected a Custom key")
}
doer.Do("write customErrorType", func() error {
return wr.WriteUint32(uint32(InstructionErrorType_CUSTOM), bin.LE)
})
customErrorTypeFloat, ok := as[firstKey].(float64)
if !ok {
return nil, fmt.Errorf("expected a float64")
case map[string]interface{}:
{
// if object, then it's custom
firstKey := getFirstKey(as)
if firstKey == "" {
return nil, fmt.Errorf("no keys found in map")
}
if firstKey != "Custom" {
return nil, fmt.Errorf("expected a Custom key")
}
doer.Do("write customErrorType", func() error {
return wr.WriteUint32(uint32(InstructionErrorType_CUSTOM), bin.LE)
})
customErrorTypeFloat, ok := as[firstKey].(float64)
if !ok {
return nil, fmt.Errorf("expected a float64")
}
customErrorType := uint32(customErrorTypeFloat)
doer.Do("write customErrorType", func() error {
return wr.WriteUint32(customErrorType, bin.LE)
})
}
customErrorType := uint32(customErrorTypeFloat)
doer.Do("write customErrorType", func() error {
return wr.WriteUint32(customErrorType, bin.LE)
})
default:
return nil, fmt.Errorf("unhandled type %T", arr[1])
}
}

}

}
err := doer.Err()
if err != nil {
return nil, err
}

err := doer.Err()
if err != nil {
return nil, err
return buf.Bytes(), nil
}

return buf.Bytes(), nil
case InsufficientFundsForRent:
doer.Do("write transactionErrorType", func() error {
return wr.WriteUint32(uint32(TransactionErrorType_INSUFFICIENT_FUNDS_FOR_RENT), bin.LE)
})
// write the accountIndex
{
// "{\"InsufficientFundsForRent\":{\"account_index\":2}}"
// read accountIndex
object, ok := j[InsufficientFundsForRent].(map[string]any)
if !ok {
return nil, fmt.Errorf("expected an object")
}
accountIndexFloat, ok := object["account_index"].(float64)
if !ok {
return nil, fmt.Errorf("expected a float64")
doer.Do("write transactionErrorType", func() error {
return wr.WriteUint32(uint32(TransactionErrorType_INSUFFICIENT_FUNDS_FOR_RENT), bin.LE)
})
// write the accountIndex
{
// "{\"InsufficientFundsForRent\":{\"account_index\":2}}"
// read accountIndex
object, ok := j[InsufficientFundsForRent].(map[string]any)
if !ok {
return nil, fmt.Errorf("expected an object")
}
accountIndexFloat, ok := object["account_index"].(float64)
if !ok {
return nil, fmt.Errorf("expected a float64")
}
accountIndex := uint8(accountIndexFloat)
doer.Do("write accountIndex", func() error {
return wr.WriteUint8(accountIndex)
})

if err := doer.Err(); err != nil {
return nil, err
}
}
accountIndex := uint8(accountIndexFloat)
doer.Do("write accountIndex", func() error {
return wr.WriteUint8(accountIndex)
return buf.Bytes(), nil
}
case ProgramExecutionTemporarilyRestricted:
{
doer.Do("write transactionErrorType", func() error {
return wr.WriteUint32(uint32(TransactionErrorType_PROGRAM_EXECUTION_TEMPORARILY_RESTRICTED), bin.LE)
})
// write the accountIndex
{
// "{\"ProgramExecutionTemporarilyRestricted\":{\"account_index\":2}}"
// read accountIndex
object, ok := j[ProgramExecutionTemporarilyRestricted].(map[string]any)
if !ok {
return nil, fmt.Errorf("expected an object")
}
accountIndexFloat, ok := object["account_index"].(float64)
if !ok {
return nil, fmt.Errorf("expected a float64")
}
accountIndex := uint8(accountIndexFloat)
doer.Do("write accountIndex", func() error {
return wr.WriteUint8(accountIndex)
})

if err := doer.Err(); err != nil {
return nil, err
if err := doer.Err(); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
case DuplicateInstruction:
{
doer.Do("write transactionErrorType", func() error {
return wr.WriteUint32(uint32(TransactionErrorType_DUPLICATE_INSTRUCTION), bin.LE)
})
// write the instruction index
{
// "{\"DuplicateInstruction\":[2]}"
// read instructionIndex
arr, ok := j[DuplicateInstruction].([]interface{})
if !ok {
return nil, fmt.Errorf("expected an array")
}
if len(arr) != 1 {
return nil, fmt.Errorf("expected an array of length 1")
}
instructionIndexFloat, ok := arr[0].(float64)
if !ok {
return nil, fmt.Errorf("expected a float64")
}
instructionIndex := uint8(instructionIndexFloat)
doer.Do("write instructionIndex", func() error {
return wr.WriteUint8(instructionIndex)
})

return buf.Bytes(), nil
if err := doer.Err(); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}

default:
return nil, fmt.Errorf("unhandled error type: %s from %q", firstKey, toJsonString(j))
// it's one of the single-value errors
{
// iterate over TransactionErrorType_name and find the matching key
var found bool
for k, v := range TransactionErrorType_name {
if bin.ToPascalCase(v) == firstKey {
doer.Do("write transactionErrorType", func() error {
return wr.WriteUint32(uint32(k), bin.LE)
})
found = true
break
}
}
if !found {
return nil, fmt.Errorf("unknown error type: %q", firstKey)
}
}

err := doer.Err()
if err != nil {
return nil, err
}

return buf.Bytes(), nil
}
}

Expand All @@ -139,11 +231,6 @@ func getFirstKey(m map[string]interface{}) string {
return ""
}

const (
InstructionError = "InstructionError"
InsufficientFundsForRent = "InsufficientFundsForRent"
)

type ChainOps struct {
e error
}
Expand Down
Loading

0 comments on commit 4808f83

Please sign in to comment.