From 05ecfc6ef279475d695b065a35ae82476ba65f13 Mon Sep 17 00:00:00 2001
From: Howard Hinnant <howard.hinnant@gmail.com>
Date: Thu, 24 Oct 2024 14:14:34 -0400
Subject: [PATCH] [FOLD] Prevent conversion from STNumber* to Number*.

* This prevents code trying to delete a STNumber via a
  pointer to the base class using a non-virtual destructor.
---
 include/xrpl/protocol/STNumber.h    |  8 +++++---
 src/libxrpl/protocol/STNumber.cpp   | 22 +++++++++++-----------
 src/test/protocol/STNumber_test.cpp |  2 ++
 3 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/include/xrpl/protocol/STNumber.h b/include/xrpl/protocol/STNumber.h
index 96fd69202b2..9a0bca21117 100644
--- a/include/xrpl/protocol/STNumber.h
+++ b/include/xrpl/protocol/STNumber.h
@@ -39,8 +39,10 @@ namespace ripple {
  * without paying the storage cost of duplicating asset information
  * that may be deduced from the context.
  */
-class STNumber : public STBase, public CountedObject<STNumber>, public Number
+class STNumber : public STBase, public CountedObject<STNumber>
 {
+    Number value_;
+
 public:
     using value_type = Number;
 
@@ -48,8 +50,6 @@ class STNumber : public STBase, public CountedObject<STNumber>, public Number
     explicit STNumber(SField const& field, Number const& value = Number());
     STNumber(SerialIter& sit, SField const& field);
 
-    using Number::operator=;
-
     SerializedTypeID
     getSType() const override;
     std::string
@@ -68,6 +68,8 @@ class STNumber : public STBase, public CountedObject<STNumber>, public Number
     bool
     isDefault() const override;
 
+    operator Number() const {return value_;}
+
 private:
     STBase*
     copy(std::size_t n, void* buf) const override;
diff --git a/src/libxrpl/protocol/STNumber.cpp b/src/libxrpl/protocol/STNumber.cpp
index 5aad7f0fcf5..2840618d416 100644
--- a/src/libxrpl/protocol/STNumber.cpp
+++ b/src/libxrpl/protocol/STNumber.cpp
@@ -24,7 +24,7 @@
 namespace ripple {
 
 STNumber::STNumber(SField const& field, Number const& value)
-    : STBase(field), Number(value)
+    : STBase(field), value_(value)
 {
 }
 
@@ -34,7 +34,7 @@ STNumber::STNumber(SerialIter& sit, SField const& field) : STBase(field)
     // to guarantee their order of execution.
     auto mantissa = sit.geti64();
     auto exponent = sit.geti32();
-    *this = Number{mantissa, exponent};
+    value_ = Number{mantissa, exponent};
 }
 
 SerializedTypeID
@@ -46,13 +46,13 @@ STNumber::getSType() const
 std::string
 STNumber::getText() const
 {
-    return to_string(*this);
+    return to_string(value_);
 }
 
 Json::Value
 STNumber::getJson(JsonOptions) const
 {
-    return to_string(*this);
+    return to_string(value_);
 }
 
 void
@@ -60,20 +60,20 @@ STNumber::add(Serializer& s) const
 {
     assert(getFName().isBinary());
     assert(getFName().fieldType == getSType());
-    s.add64(this->mantissa());
-    s.add32(this->exponent());
+    s.add64(value_.mantissa());
+    s.add32(value_.exponent());
 }
 
 Number const&
 STNumber::value() const
 {
-    return *this;
+    return value_;
 }
 
 void
 STNumber::setValue(Number const& v)
 {
-    *this = v;
+    value_ = v;
 }
 
 STBase*
@@ -92,14 +92,14 @@ bool
 STNumber::isEquivalent(STBase const& t) const
 {
     assert(t.getSType() == this->getSType());
-    Number const& v = dynamic_cast<Number const&>(t);
-    return *this == v;
+    STNumber const& v = dynamic_cast<STNumber const&>(t);
+    return value_ == v;
 }
 
 bool
 STNumber::isDefault() const
 {
-    return *this == Number();
+    return value_ == Number();
 }
 
 std::ostream&
diff --git a/src/test/protocol/STNumber_test.cpp b/src/test/protocol/STNumber_test.cpp
index b11c70722ee..ed255e32f1c 100644
--- a/src/test/protocol/STNumber_test.cpp
+++ b/src/test/protocol/STNumber_test.cpp
@@ -46,6 +46,8 @@ struct STNumber_test : public beast::unit_test::suite
     void
     run() override
     {
+        static_assert(!std::is_convertible_v<STNumber*, Number*>);
+
         {
             STNumber const stnum{sfNumber};
             BEAST_EXPECT(stnum.getSType() == STI_NUMBER);